diff --git a/.editorconfig b/.editorconfig index 3513ec6283..5d3beb0336 100644 --- a/.editorconfig +++ b/.editorconfig @@ -13,6 +13,9 @@ vsspell_dictionary_languages = en-US vsspell_section_id = 842f80e2d4aa4288afdcd0e42833eeaf vsspell_ignored_words_842f80e2d4aa4288afdcd0e42833eeaf = runsettings|nullable|args|testhost|mutex|trx|vstest|arity|async|bool|inlined|json|jsonite|jsonrpc|localhost|readonly|xml|stylecop|indices|dotnet|lifecycle +[*.{xlf,resx}] +insert_final_newline = unset + # Xml localization files [*.xlf] vsspell_spell_check_as_you_type = false @@ -35,7 +38,11 @@ charset = utf-8 indent_size = 2 # Xml files -[*.{xml}] +[*.xml] +indent_size = 2 + +# Xlf files +[*.xlf] indent_size = 2 # YAML files @@ -428,6 +435,9 @@ dotnet_diagnostic.CA1854.severity = warning # CA1863: Use 'CompositeFormat' dotnet_diagnostic.CA1863.severity = none +# CA2007: Consider calling ConfigureAwait on the awaited task +dotnet_diagnostic.CA2007.severity = warning + # CA2016: Forward the 'CancellationToken' parameter to methods dotnet_diagnostic.CA2016.severity = warning @@ -458,9 +468,6 @@ dotnet_diagnostic.CA2245.severity = warning # CA2248: Provide correct enum argument to Enum.HasFlag dotnet_diagnostic.CA2248.severity = warning -# RS0016: Only enable if API files are present -dotnet_public_api_analyzer.require_api_files = true - # RS0041: Do not use 'Obsolete' attribute dotnet_diagnostic.RS0041.severity = none @@ -683,11 +690,14 @@ csharp_style_unused_value_expression_statement_preference = discard_variable:sil # IDE0290: Use primary constructor csharp_style_prefer_primary_constructors = false +# Empty constructor +resharper_empty_constructor_highlighting = warning + # Redundant empty argument list on object creation expression resharper_redundant_empty_object_creation_argument_list_highlighting = warning -# IDE0300: Simplify collection initialization -dotnet_style_prefer_collection_expression = false +# IDE0300-IDE0306: Simplify collection initialization +dotnet_style_prefer_collection_expression = true # IDE0065: using directive placement csharp_using_directive_placement = outside_namespace:warning @@ -826,4 +836,4 @@ resharper_replace_with_single_call_to_first_or_default_highlighting = warning resharper_replace_conditional_expression_with_null_coalescing_highlighting = warning # Redundant cast -resharper_redundant_cast_highlighting = warning \ No newline at end of file +resharper_redundant_cast_highlighting = warning diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md new file mode 100644 index 0000000000..8910537ae2 --- /dev/null +++ b/.github/copilot-instructions.md @@ -0,0 +1,42 @@ +This is a .NET based repository that contains the MSTest testing framework and Microsoft.Testing.Platform (aka MTP) testing platform. Please follow these guidelines when contributing: + +## Code Standards + +You MUST follow all code-formatting and naming conventions defined in [`.editorconfig`](../.editorconfig). + +In addition to the rules enforced by `.editorconfig`, you SHOULD: + +- Favor style and conventions that are consistent with the existing codebase. +- Prefer file-scoped namespace declarations and single-line using directives. +- Ensure that the final return statement of a method is on its own line. +- Use pattern matching and switch expressions wherever possible. +- Use `nameof` instead of string literals when referring to member names. +- Always use `is null` or `is not null` instead of `== null` or `!= null`. +- Trust the C# null annotations and don't add null checks when the type system says a value cannot be null. +- Prefer `?.` if applicable (e.g. `scope?.Dispose()`). +- Use `ObjectDisposedException.ThrowIf` where applicable. +- Respect StyleCop.Analyzers rules, in particular: + - SA1028: Code must not contain trailing whitespace + - SA1316: Tuple element names should use correct casing + - SA1518: File is required to end with a single newline character + +You MUST minimize adding public API surface area but any newly added public API MUST be declared in the related `PublicAPI.Unshipped.txt` file. + +## Localization Guidelines + +Anytime you add a new localization resource, you MUST: +- Add a corresponding entry in the localization resource file. +- Add an entry in all `*.xlf` files related to the modified `.resx` file. +- Do not modify existing entries in '*.xlf' files unless you are also modifying the corresponding `.resx` file. + +## Testing Guidelines + +- Tests for MTP and MSTest analyzers MUST use MSTest. +- Unit tests for MSTest MUST use the internal test framework defined in [`TestFramework.ForTestingMSTest`](../test/Utilities/TestFramework.ForTestingMSTest). +- All assertions must be written using FluentAssertions style of assertion. + +## Pull Request guidelines + +- Let other developers discuss their comments to your PRs, unless something sounds like a direct order to you, don't do changes. +- Do the changes when you are specifically tagged or mentioned as copilot. +- If you are unsure comment with the temperature and sentiment of the comment, so we know how to efficiently address you as a member of the team rather than having to tag you. diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 07e9451eef..25bc4f05d6 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -10,4 +10,14 @@ updates: - dependency-name: "Microsoft.DotNet.Arcade.Sdk" - dependency-name: Moq commit-message: - prefix: '[main] ' \ No newline at end of file + prefix: '[main] ' + - package-ecosystem: dotnet-sdk + directory: / + schedule: + interval: weekly + day: wednesday + ignore: + - dependency-name: '*' + update-types: + - version-update:semver-major + - version-update:semver-minor diff --git a/.github/workflows/backport-base.yml b/.github/workflows/backport-base.yml index 5941ec39fb..f53a7c1cec 100644 --- a/.github/workflows/backport-base.yml +++ b/.github/workflows/backport-base.yml @@ -2,10 +2,10 @@ on: workflow_call: inputs: pr_title_template: - description: 'The template used for the PR title. Special placeholder tokens that will be replaced with a value: %target_branch%, %source_pr_title%, %source_pr_number%, %cc_users%.' + description: 'The template used for the PR title. Special placeholder tokens that will be replaced with a value: %target_branch%, %source_pr_title%, %source_pr_number%, %source_pr_author%, %cc_users%.' required: false type: string - default: '[%target_branch%] %source_pr_title%' + default: '%source_pr_title% by @%source_pr_author% in #%source_pr_number% (backport to %target_branch%)' pr_description_template: description: 'The template used for the PR description. Special placeholder tokens that will be replaced with a value: %target_branch%, %source_pr_title%, %source_pr_number%, %cc_users%.' required: false @@ -116,6 +116,7 @@ jobs: .replace(/%target_branch%/g, target_branch) .replace(/%source_pr_title%/g, context.payload.issue.title) .replace(/%source_pr_number%/g, context.payload.issue.number) + .replace(/%source_pr_author%/g, context.payload.issue.user.login) return backport_pr_title; diff --git a/.globalconfig b/.globalconfig new file mode 100644 index 0000000000..cd088247ab --- /dev/null +++ b/.globalconfig @@ -0,0 +1,10 @@ +is_global = true + +# RS0016: Only enable if API files are present +# This should NOT be in .editorconfig. +# .editorconfig is a per-file (syntax tree) concept. +# The public API analyzer will attempt to get the options for the **first tree**. +# The first tree could be a file outside of our repo (e.g, a source-only package like Polyfill) +# By having this option here in .globalconfig, we ensure it's picked up correctly regardless of what the first tree is. +# See comments in https://github.com/microsoft/testfx/pull/6230 for clarification. +dotnet_public_api_analyzer.require_api_files = true diff --git a/Build.cmd b/Build.cmd old mode 100644 new mode 100755 diff --git a/Directory.Build.props b/Directory.Build.props index 81e8489fd4..52438c9dd4 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -5,6 +5,10 @@ true + + + + false @@ -14,6 +18,7 @@ enable true enable + true @@ -36,6 +41,9 @@ embedded 0024000004800000940000000602000000240000525341310004000001000100c547cac37abd99c8db225ef2f6c8a3602f3b3606cc9891605d02baa56104f4cfc0734aa39b93bf7852f7d9266654753cc297e7d2edfe0bac1cdcf9f717241550e0a7b191195b7667bb4f64bcb8e2121380fd1d9d46ad2d92d2d15605093924cceaf74c4861eff62abf69b9291ed0a340e113be11e6a7d3113e92484cf7045cc7 + + + $(NoWarn);TPEXP @@ -62,12 +70,13 @@ - TestingPlatformRunner + Microsoft.Testing.Platform true + true diff --git a/Directory.Build.targets b/Directory.Build.targets index 5b18285eb1..0b157c8623 100644 --- a/Directory.Build.targets +++ b/Directory.Build.targets @@ -1,5 +1,12 @@ + + + + + + + @@ -22,4 +29,44 @@ + + + + + + + <_TemplateProperties>Version=$(Version) + <_TemplatePropertiesCacheFile>$(IntermediateOutputPath)$(MSBuildProjectFile).GenerateVersionSourceFile.cache + + + + + + + + + + + + + + + + + + + <_TemplateCsproj Include="$(MSBuildProjectDirectory)/BuildInfo.cs.template" Destination="$(IntermediateOutputPath)BuildInfo.cs" /> + + + + + + + + diff --git a/Directory.Packages.props b/Directory.Packages.props index b276b09c02..d4fc62a1e4 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -12,40 +12,43 @@ false - 9.0.0 + 9.5.0 17.11.4 3.11.0 4.10.0 - 3.11.0-beta1.25173.3 + 4.8.0 + 4.14.0 $(MicrosoftCodeAnalysisPublicApiAnalyzersVersion) 6.2.14 17.13.0 - 1.50.0 - 17.14.0-beta.25202.4 + 1.55.0 + 17.14.1 1.5.0-preview.24577.4 4.5.4 1.1.3-beta1.24423.1 - 3.9.0-preview.25229.4 - 1.7.0-preview.25226.1 - + + + + + + - @@ -54,16 +57,19 @@ - + - + + + + @@ -74,10 +80,9 @@ - - + - + diff --git a/MSTest.slnf b/MSTest.slnf index 111ef185e0..c629708190 100644 --- a/MSTest.slnf +++ b/MSTest.slnf @@ -1,14 +1,17 @@ { "solution": { - "path": "TestFx.sln", + "path": "TestFx.slnx", "projects": [ "samples\\FxExtensibility\\FxExtensibility.csproj", "samples\\Playground\\Playground.csproj", + "src\\Adapter\\MSTest.Engine\\MSTest.Engine.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\\Analyzers\\MSTest.GlobalConfigsGenerator\\MSTest.GlobalConfigsGenerator.csproj", + "src\\Analyzers\\MSTest.SourceGeneration\\MSTest.SourceGeneration.csproj", "src\\Package\\MSTest.Sdk\\MSTest.Sdk.csproj", "src\\Package\\MSTest\\MSTest.csproj", "src\\TestFramework\\TestFramework.Extensions\\TestFramework.Extensions.csproj", @@ -40,6 +43,7 @@ "test\\IntegrationTests\\TestAssets\\SampleFrameworkExtensions\\SampleFrameworkExtensions.csproj", "test\\IntegrationTests\\TestAssets\\SampleProjectForAssemblyResolution\\SampleProjectForAssemblyResolution.csproj", "test\\IntegrationTests\\TestAssets\\SuiteLifeCycleTestProject\\SuiteLifeCycleTestProject.csproj", + "test\\IntegrationTests\\TestAssets\\TestCategoriesFromTestDataRowProject\\TestCategoriesFromTestDataRowProject.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", @@ -50,10 +54,11 @@ "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\\MSTest.SelfRealExamples.UnitTests\\MSTest.SelfRealExamples.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 index 1595904e3a..d32084741e 100644 --- a/Microsoft.Testing.Platform.slnf +++ b/Microsoft.Testing.Platform.slnf @@ -1,6 +1,6 @@ { "solution": { - "path": "TestFx.sln", + "path": "TestFx.slnx", "projects": [ "samples\\Playground\\Playground.csproj", "src\\Platform\\Microsoft.Testing.Extensions.CrashDump\\Microsoft.Testing.Extensions.CrashDump.csproj", diff --git a/NonWindowsTests.slnf b/NonWindowsTests.slnf index e965c3ed7e..a69cbb51e1 100644 --- a/NonWindowsTests.slnf +++ b/NonWindowsTests.slnf @@ -1,14 +1,40 @@ { "solution": { - "path": "TestFx.sln", + "path": "TestFx.slnx", "projects": [ - "test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests.csproj", - "test/IntegrationTests/MSTest.Acceptance.IntegrationTests/MSTest.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.UnitTests/Microsoft.Testing.Platform.UnitTests.csproj", - "test/UnitTests/Microsoft.Testing.Platform.MSBuild.UnitTests/Microsoft.Testing.Platform.MSBuild.UnitTests.csproj", - "test/UnitTests/MSTest.Analyzers.UnitTests/MSTest.Analyzers.UnitTests.csproj" + "src\\Adapter\\MSTest.Engine\\MSTest.Engine.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\\Analyzers\\MSTest.GlobalConfigsGenerator\\MSTest.GlobalConfigsGenerator.csproj", + "src\\Analyzers\\MSTest.Internal.Analyzers\\MSTest.Internal.Analyzers.csproj", + "src\\Analyzers\\MSTest.SourceGeneration\\MSTest.SourceGeneration.csproj", + "src\\Package\\MSTest.Sdk\\MSTest.Sdk.csproj", + "src\\Package\\MSTest\\MSTest.csproj", + "src\\Platform\\Microsoft.Testing.Extensions.AzureDevOpsReport\\Microsoft.Testing.Extensions.AzureDevOpsReport.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.HotReload\\Microsoft.Testing.Extensions.HotReload.csproj", + "src\\Platform\\Microsoft.Testing.Extensions.MSBuild\\Microsoft.Testing.Extensions.MSBuild.csproj", + "src\\Platform\\Microsoft.Testing.Extensions.Retry\\Microsoft.Testing.Extensions.Retry.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", + "src\\TestFramework\\TestFramework.Extensions\\TestFramework.Extensions.csproj", + "src\\TestFramework\\TestFramework\\TestFramework.csproj", + "test\\IntegrationTests\\MSTest.Acceptance.IntegrationTests\\MSTest.Acceptance.IntegrationTests.csproj", + "test\\IntegrationTests\\Microsoft.Testing.Platform.Acceptance.IntegrationTests\\Microsoft.Testing.Platform.Acceptance.IntegrationTests.csproj", + "test\\UnitTests\\MSTest.Analyzers.UnitTests\\MSTest.Analyzers.UnitTests.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/NuGet.config b/NuGet.config index 712f79ac00..5c00c1d17d 100644 --- a/NuGet.config +++ b/NuGet.config @@ -5,6 +5,10 @@ + + + + @@ -17,6 +21,10 @@ + + + + diff --git a/TestFx.sln b/TestFx.sln deleted file mode 100644 index ef5738fbb2..0000000000 --- a/TestFx.sln +++ /dev/null @@ -1,629 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 17 -VisualStudioVersion = 17.1.31910.343 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{FF8B1B72-55A1-4FFE-809E-7B79323ED8D0}" - ProjectSection(SolutionItems) = preProject - src\.editorconfig = src\.editorconfig - src\Directory.Build.props = src\Directory.Build.props - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "2 - Adapter", "2 - Adapter", "{24088844-2107-4DB2-8F3F-CBCA94FC4B28}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MSTest.TestAdapter", "src\Adapter\MSTest.TestAdapter\MSTest.TestAdapter.csproj", "{98BA6D2C-1F3D-4636-8E1D-D4932B7A253D}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MSTestAdapter.PlatformServices", "src\Adapter\MSTestAdapter.PlatformServices\MSTestAdapter.PlatformServices.csproj", "{5D153CAA-80C2-4551-9549-6C406FCEEFB1}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "3 - TestFramework", "3 - TestFramework", "{E48AC786-E150-4F41-9A16-32F02E4493D8}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestFramework", "src\TestFramework\TestFramework\TestFramework.csproj", "{7252D9E3-267D-442C-96BC-C73AEF3241D6}" -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}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "TestUtilities", "TestUtilities", "{33D3029D-E653-4929-BB31-C714178C4BEE}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{929A3EDE-893B-4801-82BA-01FD947291CB}" - ProjectSection(SolutionItems) = preProject - .editorconfig = .editorconfig - .gitignore = .gitignore - Directory.Build.props = Directory.Build.props - Directory.Build.targets = Directory.Build.targets - Directory.Packages.props = Directory.Packages.props - global.json = global.json - Nuget.config = Nuget.config - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "5 - Package", "5 - Package", "{E374A3A6-C364-4890-B315-D60F5C682B6E}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MSTestAdapter.UnitTests", "test\UnitTests\MSTestAdapter.UnitTests\MSTestAdapter.UnitTests.csproj", "{1CEB5743-70BF-475E-91BC-0AEBD63835B7}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MSTestAdapter.PlatformServices.UnitTests", "test\UnitTests\MSTestAdapter.PlatformServices.UnitTests\MSTestAdapter.PlatformServices.UnitTests.csproj", "{599833DC-EC5A-40CA-B5CF-DEF719548EEF}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestFramework.UnitTests", "test\UnitTests\TestFramework.UnitTests\TestFramework.UnitTests.csproj", "{0A4A76DD-FEE1-4D04-926B-38E1A24A7ED2}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "eng", "eng", "{FE0DF239-0D81-46CA-9277-3342009E83F9}" - 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-windows-sdk.ps1 = eng\install-windows-sdk.ps1 - eng\verify-nupkgs.ps1 = eng\verify-nupkgs.ps1 - eng\Version.Details.xml = eng\Version.Details.xml - eng\Versions.props = eng\Versions.props - eng\write-release-notes.ps1 = eng\write-release-notes.ps1 - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{92F8E9A2-903E-4025-99BC-7DC478D5466D}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FxExtensibility", "samples\FxExtensibility\FxExtensibility.csproj", "{A82770C0-1FF5-43C7-8790-471D5E4F8D6E}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "UnitTests", "UnitTests", "{BB874DF1-44FE-415A-B634-A6B829107890}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "IntegrationTests", "IntegrationTests", "{FF69998C-C661-4EF0-804B-845675B3602E}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "TestAssets", "TestAssets", "{C9F82701-0E0F-4E61-B05B-AE387E7631F6}" - ProjectSection(SolutionItems) = preProject - test\IntegrationTests\TestAssets\Directory.Build.targets = test\IntegrationTests\TestAssets\Directory.Build.targets - EndProjectSection -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MSTest.IntegrationTests", "test\IntegrationTests\MSTest.IntegrationTests\MSTest.IntegrationTests.csproj", "{6359B7FD-5C63-487A-9467-99323C17AA10}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MSTest.VstestConsoleWrapper.IntegrationTests", "test\IntegrationTests\MSTest.VstestConsoleWrapper.IntegrationTests\MSTest.VstestConsoleWrapper.IntegrationTests.csproj", "{FE2A0AE6-835E-4603-9CE9-C8E25D14C4A0}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PlatformServices.Desktop.IntegrationTests", "test\IntegrationTests\PlatformServices.Desktop.IntegrationTests\PlatformServices.Desktop.IntegrationTests.csproj", "{93D3B3B5-6850-461C-9A08-A78D2921F86F}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DataRowTestProject", "test\IntegrationTests\TestAssets\DataRowTestProject\DataRowTestProject.csproj", "{2833FDA8-ECA0-439C-97E4-620AB9EC45AA}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DataSourceTestProject", "test\IntegrationTests\TestAssets\DataSourceTestProject\DataSourceTestProject.csproj", "{60116E14-11A3-47DF-B730-8A28ABD9B7AF}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DeploymentTestProject.Never", "test\IntegrationTests\TestAssets\DeploymentTestProject.Never\DeploymentTestProject.Never.csproj", "{CA6A4A5A-CFDE-440B-898E-FF6FBA405540}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DeploymentTestProject.PreserveNewest", "test\IntegrationTests\TestAssets\DeploymentTestProject.PreserveNewest\DeploymentTestProject.PreserveNewest.csproj", "{9C81E66B-5DFE-4130-982E-64EF8D7F6A60}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DesktopTestProjectx64Debug", "test\IntegrationTests\TestAssets\DesktopTestProjectx64Debug\DesktopTestProjectx64Debug.csproj", "{BE21907B-A4C1-41D7-A2EF-5C73A33090A0}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DesktopTestProjectx64Release", "test\IntegrationTests\TestAssets\DesktopTestProjectx64Release\DesktopTestProjectx64Release.csproj", "{F6AD3FE2-3C82-44E8-A694-C4C447EB2F10}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DesktopTestProjectx86Debug", "test\IntegrationTests\TestAssets\DesktopTestProjectx86Debug\DesktopTestProjectx86Debug.csproj", "{41DD903C-CA88-4761-831B-978EC793508F}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DesktopTestProjectx86Release", "test\IntegrationTests\TestAssets\DesktopTestProjectx86Release\DesktopTestProjectx86Release.csproj", "{93B2E07F-A41D-42DC-AF5D-47048761DFA9}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DiscoverInternalsProject", "test\IntegrationTests\TestAssets\DiscoverInternalsProject\DiscoverInternalsProject.csproj", "{48A0431B-9EFA-46DA-9AA9-D6552A7DA109}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DoNotParallelizeTestProject", "test\IntegrationTests\TestAssets\DoNotParallelizeTestProject\DoNotParallelizeTestProject.csproj", "{A2C5B6FE-A5B4-4419-A1FC-3F977DE24D2B}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DynamicDataTestProject", "test\IntegrationTests\TestAssets\DynamicDataTestProject\DynamicDataTestProject.csproj", "{D11D17B0-74DB-43B2-B5AC-7F86E0ACEFCD}" -EndProject -Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "FSharpTestProject", "test\IntegrationTests\TestAssets\FSharpTestProject\FSharpTestProject.fsproj", "{C37026D9-6525-4128-A4A1-9E3E2C195737}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FxExtensibilityTestProject", "test\IntegrationTests\TestAssets\FxExtensibilityTestProject\FxExtensibilityTestProject.csproj", "{2F4DFABA-B031-4164-A178-25F0753C9971}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HierarchyProject", "test\IntegrationTests\TestAssets\HierarchyProject\HierarchyProject.csproj", "{77CDBBF7-895B-44E1-8C86-05D81286BC3A}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LibProjectReferencedByDataSourceTest", "test\IntegrationTests\TestAssets\LibProjectReferencedByDataSourceTest\LibProjectReferencedByDataSourceTest.csproj", "{DBE6C4AF-4D79-44BB-94B5-C57FEC8B4E69}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OutputTestProject", "test\IntegrationTests\TestAssets\OutputTestProject\OutputTestProject.csproj", "{2607F3CC-3AFE-4968-B028-4F502B9F6A28}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ParallelClassesTestProject", "test\IntegrationTests\TestAssets\ParallelTestClass\ParallelClassesTestProject.csproj", "{60AE14C4-C54C-419D-AFF8-D7E49901D42B}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SampleFrameworkExtensions", "test\IntegrationTests\TestAssets\SampleFrameworkExtensions\SampleFrameworkExtensions.csproj", "{17025461-E4B9-4D4A-AD74-AF1C355E61BC}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SampleProjectForAssemblyResolution", "test\IntegrationTests\TestAssets\SampleProjectForAssemblyResolution\SampleProjectForAssemblyResolution.csproj", "{279E5DCA-3CD6-4C66-9D5F-475FDE96F3B8}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SuiteLifeCycleTestProject", "test\IntegrationTests\TestAssets\SuiteLifeCycleTestProject\SuiteLifeCycleTestProject.csproj", "{CAA66039-6747-4516-ADBA-F3D4C349E9EA}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestIdProject.DefaultStrategy", "test\IntegrationTests\TestAssets\TestIdProject.DefaultStrategy\TestIdProject.DefaultStrategy.csproj", "{8BCC6F49-CF4F-40DA-8EFC-D232C7984ABC}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestIdProject.DisplayNameStrategy", "test\IntegrationTests\TestAssets\TestIdProject.DisplayNameStrategy\TestIdProject.DisplayNameStrategy.csproj", "{34714FAD-A226-432E-9083-6BCF79D71873}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestIdProject.FullyQualifiedStrategy", "test\IntegrationTests\TestAssets\TestIdProject.FullyQualifiedTestStrategy\TestIdProject.FullyQualifiedStrategy.csproj", "{27CFE39F-3F60-47E0-9281-12CB13C7B91E}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestIdProject.LegacyStrategy", "test\IntegrationTests\TestAssets\TestIdProject.LegacyStrategy\TestIdProject.LegacyStrategy.csproj", "{DBA74878-65BF-4EE7-925A-67AF395F7D11}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestProjectForDiscovery", "test\IntegrationTests\TestAssets\TestProject\TestProjectForDiscovery.csproj", "{B3419F35-A31F-4CAA-A606-034D5DB669A2}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TimeoutTestProject", "test\IntegrationTests\TestAssets\TimeoutTestProject\TimeoutTestProject.csproj", "{4A184EC4-8BAB-498F-ABC6-300C70BF8240}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ParallelMethodsTestProject", "test\IntegrationTests\TestAssets\ParallelTestMethods\ParallelMethodsTestProject.csproj", "{CA5D66E2-7244-4E72-AFB6-58A826A30B74}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Automation.CLI", "test\Utilities\Automation.CLI\Automation.CLI.csproj", "{35E90097-976B-4FF5-B55F-2F51561D7A33}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestFramework.ForTestingMSTest", "test\Utilities\TestFramework.ForTestingMSTest\TestFramework.ForTestingMSTest.csproj", "{17CE999F-F3D1-48B4-A18F-8BA050C3A4AD}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MSTest", "src\Package\MSTest\MSTest.csproj", "{ED8534D1-3136-4915-8071-5547E8D84DC3}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "TestingPlatformRunner", "TestingPlatformRunner", "{A9F9C49E-3CDA-4207-AA53-CC80AF1798FE}" - ProjectSection(SolutionItems) = preProject - eng\TestingPlatformRunner\TestingPlatform.Runner.targets = eng\TestingPlatformRunner\TestingPlatform.Runner.targets - eng\TestingPlatformRunner\TestingPlatformRunner.targets = eng\TestingPlatformRunner\TestingPlatformRunner.targets - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "1 - Platform and Extensions", "1 - Platform and 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 -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Testing.Platform.UnitTests", "test\UnitTests\Microsoft.Testing.Platform.UnitTests\Microsoft.Testing.Platform.UnitTests.csproj", "{0F1BB08E-BB6C-43E0-A7DF-1D6A03DA5DC7}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "4 - Analyzers", "4 - Analyzers", "{E7F15C9C-3928-47AD-8462-64FD29FFCA54}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MSTest.Analyzers", "src\Analyzers\MSTest.Analyzers\MSTest.Analyzers.csproj", "{72C4FA49-E553-4B39-825A-8C4EA6CE93E2}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MSTest.Analyzers.CodeFixes", "src\Analyzers\MSTest.Analyzers.CodeFixes\MSTest.Analyzers.CodeFixes.csproj", "{462B0201-1C26-4951-97C9-722C2A58EF8C}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MSTest.Analyzers.Package", "src\Analyzers\MSTest.Analyzers.Package\MSTest.Analyzers.Package.csproj", "{DC068986-7549-4B75-8EFC-A9958FD5CF88}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MSTest.Analyzers.UnitTests", "test\UnitTests\MSTest.Analyzers.UnitTests\MSTest.Analyzers.UnitTests.csproj", "{1FF35C23-C128-4C95-B3F8-67B1B4C51E4D}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MSTest.Sdk", "src\Package\MSTest.Sdk\MSTest.Sdk.csproj", "{10930CFD-EDF9-4486-B0A3-49230B5A6798}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ClsTestProject", "test\IntegrationTests\TestAssets\ClsTestProject\ClsTestProject.csproj", "{100CF515-8291-45FF-9FFD-2A9064FECC72}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Testing.TestInfrastructure", "test\Utilities\Microsoft.Testing.TestInfrastructure\Microsoft.Testing.TestInfrastructure.csproj", "{9DCE14DA-3DFB-48DE-A3CC-A02C3465973C}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Testing.Platform.Acceptance.IntegrationTests", "test\IntegrationTests\Microsoft.Testing.Platform.Acceptance.IntegrationTests\Microsoft.Testing.Platform.Acceptance.IntegrationTests.csproj", "{3CF3861E-EB1B-4FA6-9355-372A9816DF6B}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Performance", "Performance", "{CB0CC552-2017-40C0-934A-C8A3B00EF650}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MSTest.Performance.Runner", "test\Performance\MSTest.Performance.Runner\MSTest.Performance.Runner.csproj", "{7E9D98E7-733C-4D6B-A5AC-087D588A40ED}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Playground", "samples\Playground\Playground.csproj", "{8A41B37E-0732-4F28-B214-A44233B447FE}" -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 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Testing.Extensions.MSBuild", "src\Platform\Microsoft.Testing.Extensions.MSBuild\Microsoft.Testing.Extensions.MSBuild.csproj", "{8CE782A2-7374-4916-9C69-1F87E51A64A9}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MSTest.Internal.Analyzers", "src\Analyzers\MSTest.Internal.Analyzers\MSTest.Internal.Analyzers.csproj", "{4A93E1A2-B61E-31B2-33F2-478156A9B5E7}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Testing.Extensions.HotReload", "src\Platform\Microsoft.Testing.Extensions.HotReload\Microsoft.Testing.Extensions.HotReload.csproj", "{53EBA540-F6CF-0715-1F62-241A53F537EC}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Testing.Extensions.Retry", "src\Platform\Microsoft.Testing.Extensions.Retry\Microsoft.Testing.Extensions.Retry.csproj", "{FB4ED3AA-A12E-4192-861F-4B025876AA0F}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MSTest.GlobalConfigsGenerator", "src\Analyzers\MSTest.GlobalConfigsGenerator\MSTest.GlobalConfigsGenerator.csproj", "{A85AA656-6DB6-4A0B-AA80-CBB4058B3DDB}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MSTest.Engine", "src\Adapter\MSTest.Engine\MSTest.Engine.csproj", "{82881535-7E40-80D9-F086-A3847775F2E7}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MSTest.SourceGeneration", "src\Analyzers\MSTest.SourceGeneration\MSTest.SourceGeneration.csproj", "{7BA0E74E-798E-4399-2EDE-A23BD5DA78CA}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MSTest.Engine.UnitTests", "test\UnitTests\MSTest.Engine.UnitTests\MSTest.Engine.UnitTests.csproj", "{2C0DFAC0-5D58-D172-ECE4-CBB78AD03435}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MSTest.SourceGeneration.UnitTests", "test\UnitTests\MSTest.SourceGeneration.UnitTests\MSTest.SourceGeneration.UnitTests.csproj", "{E6C0466E-BE8D-C04F-149A-FD98438F1413}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Testing.Extensions.AzureDevOpsReport", "src\Platform\Microsoft.Testing.Extensions.AzureDevOpsReport\Microsoft.Testing.Extensions.AzureDevOpsReport.csproj", "{F608D3A3-125B-CD88-1D51-8714ED142029}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {98BA6D2C-1F3D-4636-8E1D-D4932B7A253D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {98BA6D2C-1F3D-4636-8E1D-D4932B7A253D}.Debug|Any CPU.Build.0 = Debug|Any CPU - {98BA6D2C-1F3D-4636-8E1D-D4932B7A253D}.Release|Any CPU.ActiveCfg = Release|Any CPU - {98BA6D2C-1F3D-4636-8E1D-D4932B7A253D}.Release|Any CPU.Build.0 = Release|Any CPU - {5D153CAA-80C2-4551-9549-6C406FCEEFB1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {5D153CAA-80C2-4551-9549-6C406FCEEFB1}.Debug|Any CPU.Build.0 = Debug|Any CPU - {5D153CAA-80C2-4551-9549-6C406FCEEFB1}.Release|Any CPU.ActiveCfg = Release|Any CPU - {5D153CAA-80C2-4551-9549-6C406FCEEFB1}.Release|Any CPU.Build.0 = Release|Any CPU - {7252D9E3-267D-442C-96BC-C73AEF3241D6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {7252D9E3-267D-442C-96BC-C73AEF3241D6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7252D9E3-267D-442C-96BC-C73AEF3241D6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {7252D9E3-267D-442C-96BC-C73AEF3241D6}.Release|Any CPU.Build.0 = Release|Any CPU - {DF131865-84EE-4540-8112-E88ACEBDEA09}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {DF131865-84EE-4540-8112-E88ACEBDEA09}.Debug|Any CPU.Build.0 = Debug|Any CPU - {DF131865-84EE-4540-8112-E88ACEBDEA09}.Release|Any CPU.ActiveCfg = Release|Any CPU - {DF131865-84EE-4540-8112-E88ACEBDEA09}.Release|Any CPU.Build.0 = Release|Any CPU - {1CEB5743-70BF-475E-91BC-0AEBD63835B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {1CEB5743-70BF-475E-91BC-0AEBD63835B7}.Debug|Any CPU.Build.0 = Debug|Any CPU - {1CEB5743-70BF-475E-91BC-0AEBD63835B7}.Release|Any CPU.ActiveCfg = Release|Any CPU - {1CEB5743-70BF-475E-91BC-0AEBD63835B7}.Release|Any CPU.Build.0 = Release|Any CPU - {599833DC-EC5A-40CA-B5CF-DEF719548EEF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {599833DC-EC5A-40CA-B5CF-DEF719548EEF}.Debug|Any CPU.Build.0 = Debug|Any CPU - {599833DC-EC5A-40CA-B5CF-DEF719548EEF}.Release|Any CPU.ActiveCfg = Release|Any CPU - {599833DC-EC5A-40CA-B5CF-DEF719548EEF}.Release|Any CPU.Build.0 = Release|Any CPU - {0A4A76DD-FEE1-4D04-926B-38E1A24A7ED2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {0A4A76DD-FEE1-4D04-926B-38E1A24A7ED2}.Debug|Any CPU.Build.0 = Debug|Any CPU - {0A4A76DD-FEE1-4D04-926B-38E1A24A7ED2}.Release|Any CPU.ActiveCfg = Release|Any CPU - {0A4A76DD-FEE1-4D04-926B-38E1A24A7ED2}.Release|Any CPU.Build.0 = Release|Any CPU - {A82770C0-1FF5-43C7-8790-471D5E4F8D6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {A82770C0-1FF5-43C7-8790-471D5E4F8D6E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {A82770C0-1FF5-43C7-8790-471D5E4F8D6E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {A82770C0-1FF5-43C7-8790-471D5E4F8D6E}.Release|Any CPU.Build.0 = Release|Any CPU - {6359B7FD-5C63-487A-9467-99323C17AA10}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {6359B7FD-5C63-487A-9467-99323C17AA10}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6359B7FD-5C63-487A-9467-99323C17AA10}.Release|Any CPU.ActiveCfg = Release|Any CPU - {6359B7FD-5C63-487A-9467-99323C17AA10}.Release|Any CPU.Build.0 = Release|Any CPU - {FE2A0AE6-835E-4603-9CE9-C8E25D14C4A0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {FE2A0AE6-835E-4603-9CE9-C8E25D14C4A0}.Debug|Any CPU.Build.0 = Debug|Any CPU - {FE2A0AE6-835E-4603-9CE9-C8E25D14C4A0}.Release|Any CPU.ActiveCfg = Release|Any CPU - {FE2A0AE6-835E-4603-9CE9-C8E25D14C4A0}.Release|Any CPU.Build.0 = Release|Any CPU - {93D3B3B5-6850-461C-9A08-A78D2921F86F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {93D3B3B5-6850-461C-9A08-A78D2921F86F}.Debug|Any CPU.Build.0 = Debug|Any CPU - {93D3B3B5-6850-461C-9A08-A78D2921F86F}.Release|Any CPU.ActiveCfg = Release|Any CPU - {93D3B3B5-6850-461C-9A08-A78D2921F86F}.Release|Any CPU.Build.0 = Release|Any CPU - {2833FDA8-ECA0-439C-97E4-620AB9EC45AA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {2833FDA8-ECA0-439C-97E4-620AB9EC45AA}.Debug|Any CPU.Build.0 = Debug|Any CPU - {2833FDA8-ECA0-439C-97E4-620AB9EC45AA}.Release|Any CPU.ActiveCfg = Release|Any CPU - {2833FDA8-ECA0-439C-97E4-620AB9EC45AA}.Release|Any CPU.Build.0 = Release|Any CPU - {60116E14-11A3-47DF-B730-8A28ABD9B7AF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {60116E14-11A3-47DF-B730-8A28ABD9B7AF}.Debug|Any CPU.Build.0 = Debug|Any CPU - {60116E14-11A3-47DF-B730-8A28ABD9B7AF}.Release|Any CPU.ActiveCfg = Release|Any CPU - {60116E14-11A3-47DF-B730-8A28ABD9B7AF}.Release|Any CPU.Build.0 = Release|Any CPU - {CA6A4A5A-CFDE-440B-898E-FF6FBA405540}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {CA6A4A5A-CFDE-440B-898E-FF6FBA405540}.Debug|Any CPU.Build.0 = Debug|Any CPU - {CA6A4A5A-CFDE-440B-898E-FF6FBA405540}.Release|Any CPU.ActiveCfg = Release|Any CPU - {CA6A4A5A-CFDE-440B-898E-FF6FBA405540}.Release|Any CPU.Build.0 = Release|Any CPU - {9C81E66B-5DFE-4130-982E-64EF8D7F6A60}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {9C81E66B-5DFE-4130-982E-64EF8D7F6A60}.Debug|Any CPU.Build.0 = Debug|Any CPU - {9C81E66B-5DFE-4130-982E-64EF8D7F6A60}.Release|Any CPU.ActiveCfg = Release|Any CPU - {9C81E66B-5DFE-4130-982E-64EF8D7F6A60}.Release|Any CPU.Build.0 = Release|Any CPU - {BE21907B-A4C1-41D7-A2EF-5C73A33090A0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {BE21907B-A4C1-41D7-A2EF-5C73A33090A0}.Debug|Any CPU.Build.0 = Debug|Any CPU - {BE21907B-A4C1-41D7-A2EF-5C73A33090A0}.Release|Any CPU.ActiveCfg = Release|Any CPU - {BE21907B-A4C1-41D7-A2EF-5C73A33090A0}.Release|Any CPU.Build.0 = Release|Any CPU - {F6AD3FE2-3C82-44E8-A694-C4C447EB2F10}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F6AD3FE2-3C82-44E8-A694-C4C447EB2F10}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F6AD3FE2-3C82-44E8-A694-C4C447EB2F10}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F6AD3FE2-3C82-44E8-A694-C4C447EB2F10}.Release|Any CPU.Build.0 = Release|Any CPU - {41DD903C-CA88-4761-831B-978EC793508F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {41DD903C-CA88-4761-831B-978EC793508F}.Debug|Any CPU.Build.0 = Debug|Any CPU - {41DD903C-CA88-4761-831B-978EC793508F}.Release|Any CPU.ActiveCfg = Release|Any CPU - {41DD903C-CA88-4761-831B-978EC793508F}.Release|Any CPU.Build.0 = Release|Any CPU - {93B2E07F-A41D-42DC-AF5D-47048761DFA9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {93B2E07F-A41D-42DC-AF5D-47048761DFA9}.Debug|Any CPU.Build.0 = Debug|Any CPU - {93B2E07F-A41D-42DC-AF5D-47048761DFA9}.Release|Any CPU.ActiveCfg = Release|Any CPU - {93B2E07F-A41D-42DC-AF5D-47048761DFA9}.Release|Any CPU.Build.0 = Release|Any CPU - {48A0431B-9EFA-46DA-9AA9-D6552A7DA109}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {48A0431B-9EFA-46DA-9AA9-D6552A7DA109}.Debug|Any CPU.Build.0 = Debug|Any CPU - {48A0431B-9EFA-46DA-9AA9-D6552A7DA109}.Release|Any CPU.ActiveCfg = Release|Any CPU - {48A0431B-9EFA-46DA-9AA9-D6552A7DA109}.Release|Any CPU.Build.0 = Release|Any CPU - {A2C5B6FE-A5B4-4419-A1FC-3F977DE24D2B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {A2C5B6FE-A5B4-4419-A1FC-3F977DE24D2B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {A2C5B6FE-A5B4-4419-A1FC-3F977DE24D2B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {A2C5B6FE-A5B4-4419-A1FC-3F977DE24D2B}.Release|Any CPU.Build.0 = Release|Any CPU - {D11D17B0-74DB-43B2-B5AC-7F86E0ACEFCD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {D11D17B0-74DB-43B2-B5AC-7F86E0ACEFCD}.Debug|Any CPU.Build.0 = Debug|Any CPU - {D11D17B0-74DB-43B2-B5AC-7F86E0ACEFCD}.Release|Any CPU.ActiveCfg = Release|Any CPU - {D11D17B0-74DB-43B2-B5AC-7F86E0ACEFCD}.Release|Any CPU.Build.0 = Release|Any CPU - {C37026D9-6525-4128-A4A1-9E3E2C195737}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {C37026D9-6525-4128-A4A1-9E3E2C195737}.Debug|Any CPU.Build.0 = Debug|Any CPU - {C37026D9-6525-4128-A4A1-9E3E2C195737}.Release|Any CPU.ActiveCfg = Release|Any CPU - {C37026D9-6525-4128-A4A1-9E3E2C195737}.Release|Any CPU.Build.0 = Release|Any CPU - {2F4DFABA-B031-4164-A178-25F0753C9971}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {2F4DFABA-B031-4164-A178-25F0753C9971}.Debug|Any CPU.Build.0 = Debug|Any CPU - {2F4DFABA-B031-4164-A178-25F0753C9971}.Release|Any CPU.ActiveCfg = Release|Any CPU - {2F4DFABA-B031-4164-A178-25F0753C9971}.Release|Any CPU.Build.0 = Release|Any CPU - {77CDBBF7-895B-44E1-8C86-05D81286BC3A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {77CDBBF7-895B-44E1-8C86-05D81286BC3A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {77CDBBF7-895B-44E1-8C86-05D81286BC3A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {77CDBBF7-895B-44E1-8C86-05D81286BC3A}.Release|Any CPU.Build.0 = Release|Any CPU - {DBE6C4AF-4D79-44BB-94B5-C57FEC8B4E69}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {DBE6C4AF-4D79-44BB-94B5-C57FEC8B4E69}.Debug|Any CPU.Build.0 = Debug|Any CPU - {DBE6C4AF-4D79-44BB-94B5-C57FEC8B4E69}.Release|Any CPU.ActiveCfg = Release|Any CPU - {DBE6C4AF-4D79-44BB-94B5-C57FEC8B4E69}.Release|Any CPU.Build.0 = Release|Any CPU - {2607F3CC-3AFE-4968-B028-4F502B9F6A28}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {2607F3CC-3AFE-4968-B028-4F502B9F6A28}.Debug|Any CPU.Build.0 = Debug|Any CPU - {2607F3CC-3AFE-4968-B028-4F502B9F6A28}.Release|Any CPU.ActiveCfg = Release|Any CPU - {2607F3CC-3AFE-4968-B028-4F502B9F6A28}.Release|Any CPU.Build.0 = Release|Any CPU - {60AE14C4-C54C-419D-AFF8-D7E49901D42B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {60AE14C4-C54C-419D-AFF8-D7E49901D42B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {60AE14C4-C54C-419D-AFF8-D7E49901D42B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {60AE14C4-C54C-419D-AFF8-D7E49901D42B}.Release|Any CPU.Build.0 = Release|Any CPU - {17025461-E4B9-4D4A-AD74-AF1C355E61BC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {17025461-E4B9-4D4A-AD74-AF1C355E61BC}.Debug|Any CPU.Build.0 = Debug|Any CPU - {17025461-E4B9-4D4A-AD74-AF1C355E61BC}.Release|Any CPU.ActiveCfg = Release|Any CPU - {17025461-E4B9-4D4A-AD74-AF1C355E61BC}.Release|Any CPU.Build.0 = Release|Any CPU - {279E5DCA-3CD6-4C66-9D5F-475FDE96F3B8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {279E5DCA-3CD6-4C66-9D5F-475FDE96F3B8}.Debug|Any CPU.Build.0 = Debug|Any CPU - {279E5DCA-3CD6-4C66-9D5F-475FDE96F3B8}.Release|Any CPU.ActiveCfg = Release|Any CPU - {279E5DCA-3CD6-4C66-9D5F-475FDE96F3B8}.Release|Any CPU.Build.0 = Release|Any CPU - {CAA66039-6747-4516-ADBA-F3D4C349E9EA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {CAA66039-6747-4516-ADBA-F3D4C349E9EA}.Debug|Any CPU.Build.0 = Debug|Any CPU - {CAA66039-6747-4516-ADBA-F3D4C349E9EA}.Release|Any CPU.ActiveCfg = Release|Any CPU - {CAA66039-6747-4516-ADBA-F3D4C349E9EA}.Release|Any CPU.Build.0 = Release|Any CPU - {8BCC6F49-CF4F-40DA-8EFC-D232C7984ABC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {8BCC6F49-CF4F-40DA-8EFC-D232C7984ABC}.Debug|Any CPU.Build.0 = Debug|Any CPU - {8BCC6F49-CF4F-40DA-8EFC-D232C7984ABC}.Release|Any CPU.ActiveCfg = Release|Any CPU - {8BCC6F49-CF4F-40DA-8EFC-D232C7984ABC}.Release|Any CPU.Build.0 = Release|Any CPU - {34714FAD-A226-432E-9083-6BCF79D71873}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {34714FAD-A226-432E-9083-6BCF79D71873}.Debug|Any CPU.Build.0 = Debug|Any CPU - {34714FAD-A226-432E-9083-6BCF79D71873}.Release|Any CPU.ActiveCfg = Release|Any CPU - {34714FAD-A226-432E-9083-6BCF79D71873}.Release|Any CPU.Build.0 = Release|Any CPU - {27CFE39F-3F60-47E0-9281-12CB13C7B91E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {27CFE39F-3F60-47E0-9281-12CB13C7B91E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {27CFE39F-3F60-47E0-9281-12CB13C7B91E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {27CFE39F-3F60-47E0-9281-12CB13C7B91E}.Release|Any CPU.Build.0 = Release|Any CPU - {DBA74878-65BF-4EE7-925A-67AF395F7D11}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {DBA74878-65BF-4EE7-925A-67AF395F7D11}.Debug|Any CPU.Build.0 = Debug|Any CPU - {DBA74878-65BF-4EE7-925A-67AF395F7D11}.Release|Any CPU.ActiveCfg = Release|Any CPU - {DBA74878-65BF-4EE7-925A-67AF395F7D11}.Release|Any CPU.Build.0 = Release|Any CPU - {B3419F35-A31F-4CAA-A606-034D5DB669A2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {B3419F35-A31F-4CAA-A606-034D5DB669A2}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B3419F35-A31F-4CAA-A606-034D5DB669A2}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B3419F35-A31F-4CAA-A606-034D5DB669A2}.Release|Any CPU.Build.0 = Release|Any CPU - {4A184EC4-8BAB-498F-ABC6-300C70BF8240}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {4A184EC4-8BAB-498F-ABC6-300C70BF8240}.Debug|Any CPU.Build.0 = Debug|Any CPU - {4A184EC4-8BAB-498F-ABC6-300C70BF8240}.Release|Any CPU.ActiveCfg = Release|Any CPU - {4A184EC4-8BAB-498F-ABC6-300C70BF8240}.Release|Any CPU.Build.0 = Release|Any CPU - {CA5D66E2-7244-4E72-AFB6-58A826A30B74}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {CA5D66E2-7244-4E72-AFB6-58A826A30B74}.Debug|Any CPU.Build.0 = Debug|Any CPU - {CA5D66E2-7244-4E72-AFB6-58A826A30B74}.Release|Any CPU.ActiveCfg = Release|Any CPU - {CA5D66E2-7244-4E72-AFB6-58A826A30B74}.Release|Any CPU.Build.0 = Release|Any CPU - {35E90097-976B-4FF5-B55F-2F51561D7A33}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {35E90097-976B-4FF5-B55F-2F51561D7A33}.Debug|Any CPU.Build.0 = Debug|Any CPU - {35E90097-976B-4FF5-B55F-2F51561D7A33}.Release|Any CPU.ActiveCfg = Release|Any CPU - {35E90097-976B-4FF5-B55F-2F51561D7A33}.Release|Any CPU.Build.0 = Release|Any CPU - {17CE999F-F3D1-48B4-A18F-8BA050C3A4AD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {17CE999F-F3D1-48B4-A18F-8BA050C3A4AD}.Debug|Any CPU.Build.0 = Debug|Any CPU - {17CE999F-F3D1-48B4-A18F-8BA050C3A4AD}.Release|Any CPU.ActiveCfg = Release|Any CPU - {17CE999F-F3D1-48B4-A18F-8BA050C3A4AD}.Release|Any CPU.Build.0 = Release|Any CPU - {ED8534D1-3136-4915-8071-5547E8D84DC3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {ED8534D1-3136-4915-8071-5547E8D84DC3}.Debug|Any CPU.Build.0 = Debug|Any CPU - {ED8534D1-3136-4915-8071-5547E8D84DC3}.Release|Any CPU.ActiveCfg = Release|Any CPU - {ED8534D1-3136-4915-8071-5547E8D84DC3}.Release|Any CPU.Build.0 = Release|Any CPU - {48FAB979-8DA5-492E-8B3F-5DBBE82F659A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {48FAB979-8DA5-492E-8B3F-5DBBE82F659A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {48FAB979-8DA5-492E-8B3F-5DBBE82F659A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {48FAB979-8DA5-492E-8B3F-5DBBE82F659A}.Release|Any CPU.Build.0 = Release|Any CPU - {0F1BB08E-BB6C-43E0-A7DF-1D6A03DA5DC7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {0F1BB08E-BB6C-43E0-A7DF-1D6A03DA5DC7}.Debug|Any CPU.Build.0 = Debug|Any CPU - {0F1BB08E-BB6C-43E0-A7DF-1D6A03DA5DC7}.Release|Any CPU.ActiveCfg = Release|Any CPU - {0F1BB08E-BB6C-43E0-A7DF-1D6A03DA5DC7}.Release|Any CPU.Build.0 = Release|Any CPU - {72C4FA49-E553-4B39-825A-8C4EA6CE93E2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {72C4FA49-E553-4B39-825A-8C4EA6CE93E2}.Debug|Any CPU.Build.0 = Debug|Any CPU - {72C4FA49-E553-4B39-825A-8C4EA6CE93E2}.Release|Any CPU.ActiveCfg = Release|Any CPU - {72C4FA49-E553-4B39-825A-8C4EA6CE93E2}.Release|Any CPU.Build.0 = Release|Any CPU - {462B0201-1C26-4951-97C9-722C2A58EF8C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {462B0201-1C26-4951-97C9-722C2A58EF8C}.Debug|Any CPU.Build.0 = Debug|Any CPU - {462B0201-1C26-4951-97C9-722C2A58EF8C}.Release|Any CPU.ActiveCfg = Release|Any CPU - {462B0201-1C26-4951-97C9-722C2A58EF8C}.Release|Any CPU.Build.0 = Release|Any CPU - {DC068986-7549-4B75-8EFC-A9958FD5CF88}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {DC068986-7549-4B75-8EFC-A9958FD5CF88}.Debug|Any CPU.Build.0 = Debug|Any CPU - {DC068986-7549-4B75-8EFC-A9958FD5CF88}.Release|Any CPU.ActiveCfg = Release|Any CPU - {DC068986-7549-4B75-8EFC-A9958FD5CF88}.Release|Any CPU.Build.0 = Release|Any CPU - {1FF35C23-C128-4C95-B3F8-67B1B4C51E4D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {1FF35C23-C128-4C95-B3F8-67B1B4C51E4D}.Debug|Any CPU.Build.0 = Debug|Any CPU - {1FF35C23-C128-4C95-B3F8-67B1B4C51E4D}.Release|Any CPU.ActiveCfg = Release|Any CPU - {1FF35C23-C128-4C95-B3F8-67B1B4C51E4D}.Release|Any CPU.Build.0 = Release|Any CPU - {10930CFD-EDF9-4486-B0A3-49230B5A6798}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {10930CFD-EDF9-4486-B0A3-49230B5A6798}.Debug|Any CPU.Build.0 = Debug|Any CPU - {10930CFD-EDF9-4486-B0A3-49230B5A6798}.Release|Any CPU.ActiveCfg = Release|Any CPU - {10930CFD-EDF9-4486-B0A3-49230B5A6798}.Release|Any CPU.Build.0 = Release|Any CPU - {100CF515-8291-45FF-9FFD-2A9064FECC72}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {100CF515-8291-45FF-9FFD-2A9064FECC72}.Debug|Any CPU.Build.0 = Debug|Any CPU - {100CF515-8291-45FF-9FFD-2A9064FECC72}.Release|Any CPU.ActiveCfg = Release|Any CPU - {100CF515-8291-45FF-9FFD-2A9064FECC72}.Release|Any CPU.Build.0 = Release|Any CPU - {9DCE14DA-3DFB-48DE-A3CC-A02C3465973C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {9DCE14DA-3DFB-48DE-A3CC-A02C3465973C}.Debug|Any CPU.Build.0 = Debug|Any CPU - {9DCE14DA-3DFB-48DE-A3CC-A02C3465973C}.Release|Any CPU.ActiveCfg = Release|Any CPU - {9DCE14DA-3DFB-48DE-A3CC-A02C3465973C}.Release|Any CPU.Build.0 = Release|Any CPU - {3CF3861E-EB1B-4FA6-9355-372A9816DF6B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {3CF3861E-EB1B-4FA6-9355-372A9816DF6B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {3CF3861E-EB1B-4FA6-9355-372A9816DF6B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {3CF3861E-EB1B-4FA6-9355-372A9816DF6B}.Release|Any CPU.Build.0 = Release|Any CPU - {7E9D98E7-733C-4D6B-A5AC-087D588A40ED}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {7E9D98E7-733C-4D6B-A5AC-087D588A40ED}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7E9D98E7-733C-4D6B-A5AC-087D588A40ED}.Release|Any CPU.ActiveCfg = Release|Any CPU - {7E9D98E7-733C-4D6B-A5AC-087D588A40ED}.Release|Any CPU.Build.0 = Release|Any CPU - {8A41B37E-0732-4F28-B214-A44233B447FE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {8A41B37E-0732-4F28-B214-A44233B447FE}.Debug|Any CPU.Build.0 = Debug|Any CPU - {8A41B37E-0732-4F28-B214-A44233B447FE}.Release|Any CPU.ActiveCfg = Release|Any CPU - {8A41B37E-0732-4F28-B214-A44233B447FE}.Release|Any CPU.Build.0 = Release|Any CPU - {BCB42780-C559-40B6-8C4A-85EBC464AAA8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {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 - {8CE782A2-7374-4916-9C69-1F87E51A64A9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {8CE782A2-7374-4916-9C69-1F87E51A64A9}.Debug|Any CPU.Build.0 = Debug|Any CPU - {8CE782A2-7374-4916-9C69-1F87E51A64A9}.Release|Any CPU.ActiveCfg = Release|Any CPU - {8CE782A2-7374-4916-9C69-1F87E51A64A9}.Release|Any CPU.Build.0 = Release|Any CPU - {4A93E1A2-B61E-31B2-33F2-478156A9B5E7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {4A93E1A2-B61E-31B2-33F2-478156A9B5E7}.Debug|Any CPU.Build.0 = Debug|Any CPU - {4A93E1A2-B61E-31B2-33F2-478156A9B5E7}.Release|Any CPU.ActiveCfg = Release|Any CPU - {4A93E1A2-B61E-31B2-33F2-478156A9B5E7}.Release|Any CPU.Build.0 = Release|Any CPU - {53EBA540-F6CF-0715-1F62-241A53F537EC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {53EBA540-F6CF-0715-1F62-241A53F537EC}.Debug|Any CPU.Build.0 = Debug|Any CPU - {53EBA540-F6CF-0715-1F62-241A53F537EC}.Release|Any CPU.ActiveCfg = Release|Any CPU - {53EBA540-F6CF-0715-1F62-241A53F537EC}.Release|Any CPU.Build.0 = Release|Any CPU - {FB4ED3AA-A12E-4192-861F-4B025876AA0F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {FB4ED3AA-A12E-4192-861F-4B025876AA0F}.Debug|Any CPU.Build.0 = Debug|Any CPU - {FB4ED3AA-A12E-4192-861F-4B025876AA0F}.Release|Any CPU.ActiveCfg = Release|Any CPU - {FB4ED3AA-A12E-4192-861F-4B025876AA0F}.Release|Any CPU.Build.0 = Release|Any CPU - {A85AA656-6DB6-4A0B-AA80-CBB4058B3DDB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {A85AA656-6DB6-4A0B-AA80-CBB4058B3DDB}.Debug|Any CPU.Build.0 = Debug|Any CPU - {A85AA656-6DB6-4A0B-AA80-CBB4058B3DDB}.Release|Any CPU.ActiveCfg = Release|Any CPU - {A85AA656-6DB6-4A0B-AA80-CBB4058B3DDB}.Release|Any CPU.Build.0 = Release|Any CPU - {82881535-7E40-80D9-F086-A3847775F2E7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {82881535-7E40-80D9-F086-A3847775F2E7}.Debug|Any CPU.Build.0 = Debug|Any CPU - {82881535-7E40-80D9-F086-A3847775F2E7}.Release|Any CPU.ActiveCfg = Release|Any CPU - {82881535-7E40-80D9-F086-A3847775F2E7}.Release|Any CPU.Build.0 = Release|Any CPU - {7BA0E74E-798E-4399-2EDE-A23BD5DA78CA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {7BA0E74E-798E-4399-2EDE-A23BD5DA78CA}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7BA0E74E-798E-4399-2EDE-A23BD5DA78CA}.Release|Any CPU.ActiveCfg = Release|Any CPU - {7BA0E74E-798E-4399-2EDE-A23BD5DA78CA}.Release|Any CPU.Build.0 = Release|Any CPU - {2C0DFAC0-5D58-D172-ECE4-CBB78AD03435}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {2C0DFAC0-5D58-D172-ECE4-CBB78AD03435}.Debug|Any CPU.Build.0 = Debug|Any CPU - {2C0DFAC0-5D58-D172-ECE4-CBB78AD03435}.Release|Any CPU.ActiveCfg = Release|Any CPU - {2C0DFAC0-5D58-D172-ECE4-CBB78AD03435}.Release|Any CPU.Build.0 = Release|Any CPU - {E6C0466E-BE8D-C04F-149A-FD98438F1413}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E6C0466E-BE8D-C04F-149A-FD98438F1413}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E6C0466E-BE8D-C04F-149A-FD98438F1413}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E6C0466E-BE8D-C04F-149A-FD98438F1413}.Release|Any CPU.Build.0 = Release|Any CPU - {F608D3A3-125B-CD88-1D51-8714ED142029}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F608D3A3-125B-CD88-1D51-8714ED142029}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F608D3A3-125B-CD88-1D51-8714ED142029}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F608D3A3-125B-CD88-1D51-8714ED142029}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {24088844-2107-4DB2-8F3F-CBCA94FC4B28} = {FF8B1B72-55A1-4FFE-809E-7B79323ED8D0} - {98BA6D2C-1F3D-4636-8E1D-D4932B7A253D} = {24088844-2107-4DB2-8F3F-CBCA94FC4B28} - {5D153CAA-80C2-4551-9549-6C406FCEEFB1} = {24088844-2107-4DB2-8F3F-CBCA94FC4B28} - {E48AC786-E150-4F41-9A16-32F02E4493D8} = {FF8B1B72-55A1-4FFE-809E-7B79323ED8D0} - {7252D9E3-267D-442C-96BC-C73AEF3241D6} = {E48AC786-E150-4F41-9A16-32F02E4493D8} - {DF131865-84EE-4540-8112-E88ACEBDEA09} = {E48AC786-E150-4F41-9A16-32F02E4493D8} - {33D3029D-E653-4929-BB31-C714178C4BEE} = {A9596292-7E67-4566-9096-143DDAA4E8D8} - {E374A3A6-C364-4890-B315-D60F5C682B6E} = {FF8B1B72-55A1-4FFE-809E-7B79323ED8D0} - {1CEB5743-70BF-475E-91BC-0AEBD63835B7} = {BB874DF1-44FE-415A-B634-A6B829107890} - {599833DC-EC5A-40CA-B5CF-DEF719548EEF} = {BB874DF1-44FE-415A-B634-A6B829107890} - {0A4A76DD-FEE1-4D04-926B-38E1A24A7ED2} = {BB874DF1-44FE-415A-B634-A6B829107890} - {FE0DF239-0D81-46CA-9277-3342009E83F9} = {929A3EDE-893B-4801-82BA-01FD947291CB} - {A82770C0-1FF5-43C7-8790-471D5E4F8D6E} = {92F8E9A2-903E-4025-99BC-7DC478D5466D} - {BB874DF1-44FE-415A-B634-A6B829107890} = {A9596292-7E67-4566-9096-143DDAA4E8D8} - {FF69998C-C661-4EF0-804B-845675B3602E} = {A9596292-7E67-4566-9096-143DDAA4E8D8} - {C9F82701-0E0F-4E61-B05B-AE387E7631F6} = {FF69998C-C661-4EF0-804B-845675B3602E} - {6359B7FD-5C63-487A-9467-99323C17AA10} = {FF69998C-C661-4EF0-804B-845675B3602E} - {FE2A0AE6-835E-4603-9CE9-C8E25D14C4A0} = {FF69998C-C661-4EF0-804B-845675B3602E} - {93D3B3B5-6850-461C-9A08-A78D2921F86F} = {FF69998C-C661-4EF0-804B-845675B3602E} - {2833FDA8-ECA0-439C-97E4-620AB9EC45AA} = {C9F82701-0E0F-4E61-B05B-AE387E7631F6} - {60116E14-11A3-47DF-B730-8A28ABD9B7AF} = {C9F82701-0E0F-4E61-B05B-AE387E7631F6} - {CA6A4A5A-CFDE-440B-898E-FF6FBA405540} = {C9F82701-0E0F-4E61-B05B-AE387E7631F6} - {9C81E66B-5DFE-4130-982E-64EF8D7F6A60} = {C9F82701-0E0F-4E61-B05B-AE387E7631F6} - {BE21907B-A4C1-41D7-A2EF-5C73A33090A0} = {C9F82701-0E0F-4E61-B05B-AE387E7631F6} - {F6AD3FE2-3C82-44E8-A694-C4C447EB2F10} = {C9F82701-0E0F-4E61-B05B-AE387E7631F6} - {41DD903C-CA88-4761-831B-978EC793508F} = {C9F82701-0E0F-4E61-B05B-AE387E7631F6} - {93B2E07F-A41D-42DC-AF5D-47048761DFA9} = {C9F82701-0E0F-4E61-B05B-AE387E7631F6} - {48A0431B-9EFA-46DA-9AA9-D6552A7DA109} = {C9F82701-0E0F-4E61-B05B-AE387E7631F6} - {A2C5B6FE-A5B4-4419-A1FC-3F977DE24D2B} = {C9F82701-0E0F-4E61-B05B-AE387E7631F6} - {D11D17B0-74DB-43B2-B5AC-7F86E0ACEFCD} = {C9F82701-0E0F-4E61-B05B-AE387E7631F6} - {C37026D9-6525-4128-A4A1-9E3E2C195737} = {C9F82701-0E0F-4E61-B05B-AE387E7631F6} - {2F4DFABA-B031-4164-A178-25F0753C9971} = {C9F82701-0E0F-4E61-B05B-AE387E7631F6} - {77CDBBF7-895B-44E1-8C86-05D81286BC3A} = {C9F82701-0E0F-4E61-B05B-AE387E7631F6} - {DBE6C4AF-4D79-44BB-94B5-C57FEC8B4E69} = {C9F82701-0E0F-4E61-B05B-AE387E7631F6} - {2607F3CC-3AFE-4968-B028-4F502B9F6A28} = {C9F82701-0E0F-4E61-B05B-AE387E7631F6} - {60AE14C4-C54C-419D-AFF8-D7E49901D42B} = {C9F82701-0E0F-4E61-B05B-AE387E7631F6} - {17025461-E4B9-4D4A-AD74-AF1C355E61BC} = {C9F82701-0E0F-4E61-B05B-AE387E7631F6} - {279E5DCA-3CD6-4C66-9D5F-475FDE96F3B8} = {C9F82701-0E0F-4E61-B05B-AE387E7631F6} - {CAA66039-6747-4516-ADBA-F3D4C349E9EA} = {C9F82701-0E0F-4E61-B05B-AE387E7631F6} - {8BCC6F49-CF4F-40DA-8EFC-D232C7984ABC} = {C9F82701-0E0F-4E61-B05B-AE387E7631F6} - {34714FAD-A226-432E-9083-6BCF79D71873} = {C9F82701-0E0F-4E61-B05B-AE387E7631F6} - {27CFE39F-3F60-47E0-9281-12CB13C7B91E} = {C9F82701-0E0F-4E61-B05B-AE387E7631F6} - {DBA74878-65BF-4EE7-925A-67AF395F7D11} = {C9F82701-0E0F-4E61-B05B-AE387E7631F6} - {B3419F35-A31F-4CAA-A606-034D5DB669A2} = {C9F82701-0E0F-4E61-B05B-AE387E7631F6} - {4A184EC4-8BAB-498F-ABC6-300C70BF8240} = {C9F82701-0E0F-4E61-B05B-AE387E7631F6} - {CA5D66E2-7244-4E72-AFB6-58A826A30B74} = {C9F82701-0E0F-4E61-B05B-AE387E7631F6} - {35E90097-976B-4FF5-B55F-2F51561D7A33} = {33D3029D-E653-4929-BB31-C714178C4BEE} - {17CE999F-F3D1-48B4-A18F-8BA050C3A4AD} = {33D3029D-E653-4929-BB31-C714178C4BEE} - {ED8534D1-3136-4915-8071-5547E8D84DC3} = {E374A3A6-C364-4890-B315-D60F5C682B6E} - {A9F9C49E-3CDA-4207-AA53-CC80AF1798FE} = {FE0DF239-0D81-46CA-9277-3342009E83F9} - {6AEE1440-FDF0-4729-8196-B24D0E333550} = {FF8B1B72-55A1-4FFE-809E-7B79323ED8D0} - {48FAB979-8DA5-492E-8B3F-5DBBE82F659A} = {6AEE1440-FDF0-4729-8196-B24D0E333550} - {0F1BB08E-BB6C-43E0-A7DF-1D6A03DA5DC7} = {BB874DF1-44FE-415A-B634-A6B829107890} - {E7F15C9C-3928-47AD-8462-64FD29FFCA54} = {FF8B1B72-55A1-4FFE-809E-7B79323ED8D0} - {72C4FA49-E553-4B39-825A-8C4EA6CE93E2} = {E7F15C9C-3928-47AD-8462-64FD29FFCA54} - {462B0201-1C26-4951-97C9-722C2A58EF8C} = {E7F15C9C-3928-47AD-8462-64FD29FFCA54} - {DC068986-7549-4B75-8EFC-A9958FD5CF88} = {E7F15C9C-3928-47AD-8462-64FD29FFCA54} - {1FF35C23-C128-4C95-B3F8-67B1B4C51E4D} = {BB874DF1-44FE-415A-B634-A6B829107890} - {10930CFD-EDF9-4486-B0A3-49230B5A6798} = {E374A3A6-C364-4890-B315-D60F5C682B6E} - {100CF515-8291-45FF-9FFD-2A9064FECC72} = {C9F82701-0E0F-4E61-B05B-AE387E7631F6} - {9DCE14DA-3DFB-48DE-A3CC-A02C3465973C} = {33D3029D-E653-4929-BB31-C714178C4BEE} - {3CF3861E-EB1B-4FA6-9355-372A9816DF6B} = {FF69998C-C661-4EF0-804B-845675B3602E} - {CB0CC552-2017-40C0-934A-C8A3B00EF650} = {A9596292-7E67-4566-9096-143DDAA4E8D8} - {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} - {8CE782A2-7374-4916-9C69-1F87E51A64A9} = {6AEE1440-FDF0-4729-8196-B24D0E333550} - {4A93E1A2-B61E-31B2-33F2-478156A9B5E7} = {E7F15C9C-3928-47AD-8462-64FD29FFCA54} - {53EBA540-F6CF-0715-1F62-241A53F537EC} = {6AEE1440-FDF0-4729-8196-B24D0E333550} - {FB4ED3AA-A12E-4192-861F-4B025876AA0F} = {6AEE1440-FDF0-4729-8196-B24D0E333550} - {A85AA656-6DB6-4A0B-AA80-CBB4058B3DDB} = {E7F15C9C-3928-47AD-8462-64FD29FFCA54} - {82881535-7E40-80D9-F086-A3847775F2E7} = {24088844-2107-4DB2-8F3F-CBCA94FC4B28} - {7BA0E74E-798E-4399-2EDE-A23BD5DA78CA} = {E7F15C9C-3928-47AD-8462-64FD29FFCA54} - {2C0DFAC0-5D58-D172-ECE4-CBB78AD03435} = {BB874DF1-44FE-415A-B634-A6B829107890} - {E6C0466E-BE8D-C04F-149A-FD98438F1413} = {BB874DF1-44FE-415A-B634-A6B829107890} - {F608D3A3-125B-CD88-1D51-8714ED142029} = {6AEE1440-FDF0-4729-8196-B24D0E333550} - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {31E0F4D5-975A-41CC-933E-545B2201FAF9} - EndGlobalSection -EndGlobal diff --git a/TestFx.slnx b/TestFx.slnx new file mode 100644 index 0000000000..d1a4ea3a97 --- /dev/null +++ b/TestFx.slnx @@ -0,0 +1,138 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/azure-pipelines-official.yml b/azure-pipelines-official.yml index 2ead4ae47b..982eec0b70 100644 --- a/azure-pipelines-official.yml +++ b/azure-pipelines-official.yml @@ -140,13 +140,16 @@ 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 + /bl:$(BUILD.SOURCESDIRECTORY)\artifacts\TestResults\$(_BuildConfig)\TestStep.binlog name: Test displayName: Test + env: + TESTINGPLATFORM_DEFAULT_HANG_TIMEOUT: 5m + DOTNET_ROOT: $(Build.SourcesDirectory)/.dotnet - task: CopyFiles@2 displayName: 'Copy binlogs' @@ -169,25 +172,27 @@ extends: Remove-Item -Path $(Build.SourcesDirectory)/artifacts/tmp -Recurse -Force displayName: Remove artifacts/tmp - # This step is only helpful for diagnosing some issues with vstest/test host that would not appear - # through the console or trx - task: 1ES.PublishBuildArtifacts@1 displayName: 'Publish Test Results folders' inputs: PathtoPublish: '$(Build.SourcesDirectory)/artifacts/TestResults/$(_BuildConfig)' - ArtifactName: TestResults_Windows - condition: failed() + ArtifactName: TestResults_Windows_Attempt$(System.JobAttempt) + condition: always() - task: NuGetAuthenticate@1 displayName: 'NuGet Authenticate to test-tools feed' - - task: 1ES.PublishNuget@1 - displayName: 'Publish NuGet packages to test-tools feed' - inputs: - # Do not push symbol packages nor Microsoft.Testing.Platform package - packageParentPath: '$(Build.SourcesDirectory)/artifacts/packages/$(_BuildConfig)' - packagesToPush: '$(Build.SourcesDirectory)/artifacts/packages/$(_BuildConfig)/**/*.nupkg;!$(Build.SourcesDirectory)/artifacts/packages/$(_BuildConfig)/**/*.symbols.nupkg;!$(Build.SourcesDirectory)/artifacts/packages/$(_BuildConfig)/NonShipping/Microsoft.Testing.Platform.*.nupkg' - publishVstsFeed: 'public/test-tools' + # Final builds should not go into test-tools package feed, so we can keep re-building them until we are ready to ship to nuget.org. + # This has to depend on the parameter and not on variable, variables are not defined early enough to be used in templating condition. + # We still need the final builds on test-tools so that internal repos can consume it, so we will publish it manually. + - ${{ if eq(parameters.isRTM, False) }}: + - task: 1ES.PublishNuget@1 + displayName: 'Publish NuGet packages to test-tools feed' + inputs: + # Do not push symbol packages nor Microsoft.Testing.Platform package + packageParentPath: '$(Build.SourcesDirectory)/artifacts/packages/$(_BuildConfig)' + packagesToPush: '$(Build.SourcesDirectory)/artifacts/packages/$(_BuildConfig)/**/*.nupkg;!$(Build.SourcesDirectory)/artifacts/packages/$(_BuildConfig)/**/*.symbols.nupkg;!$(Build.SourcesDirectory)/artifacts/packages/$(_BuildConfig)/NonShipping/Microsoft.Testing.Platform.*.nupkg' + publishVstsFeed: 'public/test-tools' - job: Linux timeoutInMinutes: 90 @@ -208,21 +213,21 @@ 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 --nobl + ./test.sh --configuration $(_BuildConfig) --ci --test --integrationTest --nobl /bl:$(BUILD.SOURCESDIRECTORY)/artifacts/TestResults/$(_BuildConfig)/TestStep.binlog name: Test displayName: Tests + env: + TESTINGPLATFORM_DEFAULT_HANG_TIMEOUT: 5m + DOTNET_ROOT: $(Build.SourcesDirectory)/.dotnet - # This step is only helpful for diagnosing some issues with vstest/test host that would not appear - # through the console or trx - task: 1ES.PublishBuildArtifacts@1 displayName: 'Publish Test Results folders' inputs: PathtoPublish: '$(Build.SourcesDirectory)/artifacts/TestResults/$(_BuildConfig)' - ArtifactName: TestResults_Linux - condition: failed() + ArtifactName: TestResults_Linux_Attempt$(System.JobAttempt) + condition: always() - ${{ if eq(variables['Build.SourceBranchName'], 'main') }}: - template: /eng/common/templates-official/job/onelocbuild.yml@self diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 1a37097e89..8ec5bd75e4 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -1,3 +1,9 @@ +trigger: + branches: + include: + - main + - rel/* + # Branch(es) that trigger(s) build(s) on PR pr: branches: @@ -16,6 +22,7 @@ pr: - README.md - SECURITY.md - src/**/*.xlf + - azure-pipelines-official.yml parameters: # This option should be used with caution. This is useful for unblocking circular deps issue with testanywhere @@ -76,7 +83,15 @@ stages: filePath: ./eng/install-procdump.ps1 failOnStderr: true showWarnings: true - + - task: PowerShell@2 + displayName: 'Enable local dumps' + inputs: + targetType: 'inline' + script: | + New-Item -Path $(Build.SourcesDirectory)\artifacts\CrashDumps -ItemType Directory -Force + Get-ChildItem "HKLM:\SOFTWARE\Microsoft\Windows\Windows Error Reporting\LocalDumps" + New-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows\Windows Error Reporting\LocalDumps" -Name "DumpFolder" -Value "$(Build.SourcesDirectory)\artifacts\CrashDumps" -PropertyType ExpandString -Force + New-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows\Windows Error Reporting\LocalDumps" -Name "DumpCount" -Value 10 -PropertyType DWord -Force - task: PowerShell@2 displayName: 'Install Access Database Engine' inputs: @@ -88,31 +103,38 @@ stages: - script: eng\common\CIBuild.cmd -configuration $(_BuildConfig) -prepareMachine + /p:Publish=false /p:Test=false /p:FastAcceptanceTest=true name: Build displayName: Build + - task: PublishBuildArtifacts@1 + displayName: 'Publish NuGet packages' + inputs: + PathtoPublish: '$(Build.SourcesDirectory)/artifacts/packages/$(_BuildConfig)/Shipping' + ArtifactName: '$(Agent.Os)_$(Agent.JobName)_Attempt$(System.JobAttempt)' + condition: always() + - ${{ if eq(parameters.SkipTests, False) }}: # Because the build step is using -ci flag, restore is done in a local .packages directory. # We need to pass NUGET_PACKAGES so that when dotnet test is doing evaluation phase on the projects, it can resolve .props/.targets from packages and import them. # Otherwise, props/targets are not imported. It's important that they are imported so that IsTestingPlatformApplication ends up being set. - - script: $(Build.SourcesDirectory)/.dotnet/dotnet test -c $(_BuildConfig) --no-build -bl:$(BUILD.SOURCESDIRECTORY)\artifacts\log\$(_BuildConfig)\TestStep.binlog + - script: dotnet test -c $(_BuildConfig) --no-build -bl:$(BUILD.SOURCESDIRECTORY)\artifacts\TestResults\$(_BuildConfig)\TestStep.binlog --no-progress -p:UsingDotNetTest=true name: Test displayName: Test env: DOTNET_ROOT: $(Build.SourcesDirectory)/.dotnet NUGET_PACKAGES: $(Build.SourcesDirectory)/.packages + DOTNET_CLI_CONTEXT_VERBOSE: 1 - # This step is only helpful for diagnosing some issues with vstest/test host that would not appear - # through the console or trx - task: PublishBuildArtifacts@1 displayName: 'Publish Test Results folders' inputs: PathtoPublish: '$(Build.SourcesDirectory)/artifacts/TestResults/$(_BuildConfig)' - ArtifactName: TestResults_Windows_$(_BuildConfig) - condition: failed() + ArtifactName: TestResults_Windows_$(_BuildConfig)_Attempt$(System.JobAttempt) + condition: always() - task: CopyFiles@2 displayName: 'Copy binlogs' @@ -131,13 +153,20 @@ stages: condition: always() # Upload code coverage to codecov.io - - script: $(Build.SourcesDirectory)/.dotnet/dotnet msbuild -restore + - script: dotnet msbuild -restore eng/CodeCoverage.proj /p:Configuration=$(_BuildConfig) /bl:$(BUILD.SOURCESDIRECTORY)\artifacts\log\$(_BuildConfig)\CodeCoverage.binlog displayName: Upload coverage to codecov.io condition: and(succeeded(), eq(variables._BuildConfig, 'Debug')) + - task: PublishBuildArtifacts@1 + displayName: 'Publish local dumps' + inputs: + PathtoPublish: '$(Build.SourcesDirectory)/artifacts/CrashDumps' + ArtifactName: TestResults_Windows_$(_BuildConfig) + condition: failed() + - job: Linux timeoutInMinutes: 90 pool: @@ -154,29 +183,36 @@ stages: -configuration $(_BuildConfig) -prepareMachine /p:Test=false + /p:Publish=false /p:NonWindowsBuild=true /p:FastAcceptanceTest=true displayName: Build + - task: PublishBuildArtifacts@1 + displayName: 'Publish NuGet packages' + inputs: + PathtoPublish: '$(Build.SourcesDirectory)/artifacts/packages/$(_BuildConfig)/Shipping' + ArtifactName: '$(Agent.Os)_$(Agent.JobName)_Attempt$(System.JobAttempt)' + condition: always() + - ${{ if eq(parameters.SkipTests, False) }}: # Because the build step is using -ci flag, restore is done in a local .packages directory. # We need to pass NUGET_PACKAGES so that when dotnet test is doing evaluation phase on the projects, it can resolve .props/.targets from packages and import them. # Otherwise, props/targets are not imported. It's important that they are imported so that IsTestingPlatformApplication ends up being set. - - script: $(Build.SourcesDirectory)/.dotnet/dotnet test --solution NonWindowsTests.slnf -c $(_BuildConfig) --no-build -bl:$(BUILD.SOURCESDIRECTORY)\artifacts\log\$(_BuildConfig)\TestStep.binlog + - script: dotnet test --solution NonWindowsTests.slnf -c $(_BuildConfig) --no-build -bl:$(BUILD.SOURCESDIRECTORY)/artifacts/TestResults/$(_BuildConfig)/TestStep.binlog --no-progress -p:UsingDotNetTest=true name: Test displayName: Test env: DOTNET_ROOT: $(Build.SourcesDirectory)/.dotnet NUGET_PACKAGES: $(Build.SourcesDirectory)/.packages + DOTNET_CLI_CONTEXT_VERBOSE: 1 - # This step is only helpful for diagnosing some issues with vstest/test host that would not appear - # through the console or trx - task: PublishBuildArtifacts@1 displayName: 'Publish Test Results folders' inputs: PathtoPublish: '$(Build.SourcesDirectory)/artifacts/TestResults/$(_BuildConfig)' - ArtifactName: TestResults_Linux_$(_BuildConfig) - condition: failed() + ArtifactName: TestResults_Linux_$(_BuildConfig)_Attempt$(System.JobAttempt) + condition: always() - task: CopyFiles@2 displayName: 'Copy binlogs' @@ -211,29 +247,36 @@ stages: -configuration $(_BuildConfig) -prepareMachine /p:Test=false + /p:Publish=false /p:NonWindowsBuild=true /p:FastAcceptanceTest=true displayName: Build + - task: PublishBuildArtifacts@1 + displayName: 'Publish NuGet packages' + inputs: + PathtoPublish: '$(Build.SourcesDirectory)/artifacts/packages/$(_BuildConfig)/Shipping' + ArtifactName: '$(Agent.Os)_$(Agent.JobName)_Attempt$(System.JobAttempt)' + condition: always() + - ${{ if eq(parameters.SkipTests, False) }}: # Because the build step is using -ci flag, restore is done in a local .packages directory. # We need to pass NUGET_PACKAGES so that when dotnet test is doing evaluation phase on the projects, it can resolve .props/.targets from packages and import them. # Otherwise, props/targets are not imported. It's important that they are imported so that IsTestingPlatformApplication ends up being set. - - script: $(Build.SourcesDirectory)/.dotnet/dotnet test --solution NonWindowsTests.slnf -c $(_BuildConfig) --no-build -bl:$(BUILD.SOURCESDIRECTORY)\artifacts\log\$(_BuildConfig)\TestStep.binlog + - script: dotnet test --solution NonWindowsTests.slnf -c $(_BuildConfig) --no-build -bl:$(BUILD.SOURCESDIRECTORY)/artifacts/TestResults/$(_BuildConfig)/TestStep.binlog --no-progress -p:UsingDotNetTest=true name: Test displayName: Test env: DOTNET_ROOT: $(Build.SourcesDirectory)/.dotnet NUGET_PACKAGES: $(Build.SourcesDirectory)/.packages + DOTNET_CLI_CONTEXT_VERBOSE: 1 - # This step is only helpful for diagnosing some issues with vstest/test host that would not appear - # through the console or trx - task: PublishBuildArtifacts@1 displayName: 'Publish Test Results folders' inputs: PathtoPublish: '$(Build.SourcesDirectory)/artifacts/TestResults/$(_BuildConfig)' - ArtifactName: TestResults_MacOs_$(_BuildConfig) - condition: failed() + ArtifactName: TestResults_MacOs_$(_BuildConfig)_Attempt$(System.JobAttempt) + condition: always() - task: CopyFiles@2 displayName: 'Copy binlogs' diff --git a/docs/Changelog-Platform.md b/docs/Changelog-Platform.md index 766d06c90e..edd71dc4ac 100644 --- a/docs/Changelog-Platform.md +++ b/docs/Changelog-Platform.md @@ -4,9 +4,335 @@ 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.1.0/) +## [1.8.5] - 2025-09-30 + +See full log [of v3.10.4...v3.10.5](https://github.com/microsoft/testfx/compare/v3.10.4...v3.10.5) + +### Fixed + +* Avoid long path issues for pipes by @Youssef1313 in [#6536](https://github.com/microsoft/testfx/pull/6536) +* Clean TestProgressState on TestExecutionCompleted to fix HotReload by @Youssef1313 in [#6505](https://github.com/microsoft/testfx/pull/6551) + +### Artifacts + +* Microsoft.Testing.Platform: [1.8.5](https://www.nuget.org/packages/Microsoft.Testing.Platform/1.8.5) +* Microsoft.Testing.Platform.MSBuild: [1.8.5](https://www.nuget.org/packages/Microsoft.Testing.Platform.MSBuild/1.8.5) +* Microsoft.Testing.Extensions.CrashDump: [1.8.5](https://www.nuget.org/packages/Microsoft.Testing.Extensions.CrashDump/1.8.5) +* Microsoft.Testing.Extensions.HangDump: [1.8.5](https://www.nuget.org/packages/Microsoft.Testing.Extensions.HangDump/1.8.5) +* Microsoft.Testing.Extensions.HotReload: [1.8.5](https://www.nuget.org/packages/Microsoft.Testing.Extensions.HotReload/1.8.5) +* Microsoft.Testing.Extensions.Retry: [1.8.5](https://www.nuget.org/packages/Microsoft.Testing.Extensions.Retry/1.8.5) +* Microsoft.Testing.Extensions.Telemetry: [1.8.5](https://www.nuget.org/packages/Microsoft.Testing.Extensions.Telemetry/1.8.5) +* Microsoft.Testing.Extensions.TrxReport: [1.8.5](https://www.nuget.org/packages/Microsoft.Testing.Extensions.TrxReport/1.8.5) +* Microsoft.Testing.Extensions.TrxReport.Abstractions: [1.8.5](https://www.nuget.org/packages/Microsoft.Testing.Extensions.TrxReport.Abstractions/1.8.5) +* Microsoft.Testing.Extensions.VSTestBridge: [1.8.5](https://www.nuget.org/packages/Microsoft.Testing.Extensions.VSTestBridge/1.8.5) +* Microsoft.Testing.Extensions.AzureDevOpsReport [1.0.0-alpha.25480.6](https://www.nuget.org/packages/Microsoft.Testing.Extensions.AzureDevOpsReport/1.0.0-alpha.25480.6) + +## [1.8.4] - 2025-09-02 + +See full log [of v3.10.3...v3.10.4](https://github.com/microsoft/testfx/compare/v3.10.3...v3.10.4) + +### Fixed + +* Fix command-line arguments escaping when retry and test host controllers start child processes by @Youssef1313 in [#6462](https://github.com/microsoft/testfx/pull/6462) +* Don't keep MTP process alive if pipe disconnects by @Youssef1313 in [#6477](https://github.com/microsoft/testfx/pull/6477) + +### Artifacts + +* Microsoft.Testing.Platform: [1.8.4](https://www.nuget.org/packages/Microsoft.Testing.Platform/1.8.4) +* Microsoft.Testing.Platform.MSBuild: [1.8.4](https://www.nuget.org/packages/Microsoft.Testing.Platform.MSBuild/1.8.4) +* Microsoft.Testing.Extensions.CrashDump: [1.8.4](https://www.nuget.org/packages/Microsoft.Testing.Extensions.CrashDump/1.8.4) +* Microsoft.Testing.Extensions.HangDump: [1.8.4](https://www.nuget.org/packages/Microsoft.Testing.Extensions.HangDump/1.8.4) +* Microsoft.Testing.Extensions.HotReload: [1.8.4](https://www.nuget.org/packages/Microsoft.Testing.Extensions.HotReload/1.8.4) +* Microsoft.Testing.Extensions.Retry: [1.8.4](https://www.nuget.org/packages/Microsoft.Testing.Extensions.Retry/1.8.4) +* Microsoft.Testing.Extensions.Telemetry: [1.8.4](https://www.nuget.org/packages/Microsoft.Testing.Extensions.Telemetry/1.8.4) +* Microsoft.Testing.Extensions.TrxReport: [1.8.4](https://www.nuget.org/packages/Microsoft.Testing.Extensions.TrxReport/1.8.4) +* Microsoft.Testing.Extensions.TrxReport.Abstractions: [1.8.4](https://www.nuget.org/packages/Microsoft.Testing.Extensions.TrxReport.Abstractions/1.8.4) +* Microsoft.Testing.Extensions.VSTestBridge: [1.8.4](https://www.nuget.org/packages/Microsoft.Testing.Extensions.VSTestBridge/1.8.4) +* Microsoft.Testing.Extensions.AzureDevOpsReport [1.0.0-alpha.25452.8](https://www.nuget.org/packages/Microsoft.Testing.Extensions.AzureDevOpsReport/1.0.0-alpha.25452.8) + +## [1.8.3] - 2025-08-26 + +See full log [of v3.10.2...v3.10.3](https://github.com/microsoft/testfx/compare/v3.10.2...v3.10.3) + +### Fixed + +* Unify reading runsettings by @Youssef1313 in [#6434](https://github.com/microsoft/testfx/pull/6434) + +### Artifacts + +* Microsoft.Testing.Platform: [1.8.3](https://www.nuget.org/packages/Microsoft.Testing.Platform/1.8.3) +* Microsoft.Testing.Platform.MSBuild: [1.8.3](https://www.nuget.org/packages/Microsoft.Testing.Platform.MSBuild/1.8.3) +* Microsoft.Testing.Extensions.CrashDump: [1.8.3](https://www.nuget.org/packages/Microsoft.Testing.Extensions.CrashDump/1.8.3) +* Microsoft.Testing.Extensions.HangDump: [1.8.3](https://www.nuget.org/packages/Microsoft.Testing.Extensions.HangDump/1.8.3) +* Microsoft.Testing.Extensions.HotReload: [1.8.3](https://www.nuget.org/packages/Microsoft.Testing.Extensions.HotReload/1.8.3) +* Microsoft.Testing.Extensions.Retry: [1.8.3](https://www.nuget.org/packages/Microsoft.Testing.Extensions.Retry/1.8.3) +* Microsoft.Testing.Extensions.Telemetry: [1.8.3](https://www.nuget.org/packages/Microsoft.Testing.Extensions.Telemetry/1.8.3) +* Microsoft.Testing.Extensions.TrxReport: [1.8.3](https://www.nuget.org/packages/Microsoft.Testing.Extensions.TrxReport/1.8.3) +* Microsoft.Testing.Extensions.TrxReport.Abstractions: [1.8.3](https://www.nuget.org/packages/Microsoft.Testing.Extensions.TrxReport.Abstractions/1.8.3) +* Microsoft.Testing.Extensions.VSTestBridge: [1.8.3](https://www.nuget.org/packages/Microsoft.Testing.Extensions.VSTestBridge/1.8.3) +* Microsoft.Testing.Extensions.AzureDevOpsReport [1.0.0-alpha.25425.9](https://www.nuget.org/packages/Microsoft.Testing.Extensions.AzureDevOpsReport/1.0.0-alpha.25425.9) + +## [1.8.2] - 2025-08-12 + +See full log [of v3.10.1...v3.10.2](https://github.com/microsoft/testfx/compare/v3.10.1...v3.10.2) + +* No change, released to keep version aligned with MSTest. + +### Artifacts + +* Microsoft.Testing.Platform: [1.8.2](https://www.nuget.org/packages/Microsoft.Testing.Platform/1.8.2) +* Microsoft.Testing.Platform.MSBuild: [1.8.2](https://www.nuget.org/packages/Microsoft.Testing.Platform.MSBuild/1.8.2) +* Microsoft.Testing.Extensions.CrashDump: [1.8.2](https://www.nuget.org/packages/Microsoft.Testing.Extensions.CrashDump/1.8.2) +* Microsoft.Testing.Extensions.HangDump: [1.8.2](https://www.nuget.org/packages/Microsoft.Testing.Extensions.HangDump/1.8.2) +* Microsoft.Testing.Extensions.HotReload: [1.8.2](https://www.nuget.org/packages/Microsoft.Testing.Extensions.HotReload/1.8.2) +* Microsoft.Testing.Extensions.Retry: [1.8.2](https://www.nuget.org/packages/Microsoft.Testing.Extensions.Retry/1.8.2) +* Microsoft.Testing.Extensions.Telemetry: [1.8.2](https://www.nuget.org/packages/Microsoft.Testing.Extensions.Telemetry/1.8.2) +* Microsoft.Testing.Extensions.TrxReport: [1.8.2](https://www.nuget.org/packages/Microsoft.Testing.Extensions.TrxReport/1.8.2) +* Microsoft.Testing.Extensions.TrxReport.Abstractions: [1.8.2](https://www.nuget.org/packages/Microsoft.Testing.Extensions.TrxReport.Abstractions/1.8.2) +* Microsoft.Testing.Extensions.VSTestBridge: [1.8.2](https://www.nuget.org/packages/Microsoft.Testing.Extensions.VSTestBridge/1.8.2) +* Microsoft.Testing.Extensions.AzureDevOpsReport [1.0.0-alpha.25411.2](https://www.nuget.org/packages/Microsoft.Testing.Extensions.AzureDevOpsReport/1.0.0-alpha.25411.2) + +## [1.8.1] - 2025-08-05 + +See full log [of v3.10.0...v3.10.1](https://github.com/microsoft/testfx/compare/v3.10.0...v3.10.1) + +* No change, released to keep version aligned with MSTest. + +### Artifacts + +* Microsoft.Testing.Platform: [1.8.1](https://www.nuget.org/packages/Microsoft.Testing.Platform/1.8.1) +* Microsoft.Testing.Platform.MSBuild: [1.8.1](https://www.nuget.org/packages/Microsoft.Testing.Platform.MSBuild/1.8.1) +* Microsoft.Testing.Extensions.CrashDump: [1.8.1](https://www.nuget.org/packages/Microsoft.Testing.Extensions.CrashDump/1.8.1) +* Microsoft.Testing.Extensions.HangDump: [1.8.1](https://www.nuget.org/packages/Microsoft.Testing.Extensions.HangDump/1.8.1) +* Microsoft.Testing.Extensions.HotReload: [1.8.1](https://www.nuget.org/packages/Microsoft.Testing.Extensions.HotReload/1.8.1) +* Microsoft.Testing.Extensions.Retry: [1.8.1](https://www.nuget.org/packages/Microsoft.Testing.Extensions.Retry/1.8.1) +* Microsoft.Testing.Extensions.Telemetry: [1.8.1](https://www.nuget.org/packages/Microsoft.Testing.Extensions.Telemetry/1.8.1) +* Microsoft.Testing.Extensions.TrxReport: [1.8.1](https://www.nuget.org/packages/Microsoft.Testing.Extensions.TrxReport/1.8.1) +* Microsoft.Testing.Extensions.TrxReport.Abstractions: [1.8.1](https://www.nuget.org/packages/Microsoft.Testing.Extensions.TrxReport.Abstractions/1.8.1) +* Microsoft.Testing.Extensions.VSTestBridge: [1.8.1](https://www.nuget.org/packages/Microsoft.Testing.Extensions.VSTestBridge/1.8.1) +* Microsoft.Testing.Extensions.AzureDevOpsReport [1.0.0-alpha.25405.1](https://www.nuget.org/packages/Microsoft.Testing.Extensions.AzureDevOpsReport/1.0.0-alpha.25405.1) + +## [1.8.0] - 2025-07-29 + +See full log [of v3.9.3...v3.10.0](https://github.com/microsoft/testfx/compare/v3.9.3...v3.10.0) + +### Added + +* Enable filtering with `TestMetadataProperty` in the treenode filter by @thomhurst in [#5679](https://github.com/microsoft/testfx/pull/5679) +* Show colors in AzDo and GH actions by @nohwnd in [#5535](https://github.com/microsoft/testfx/pull/5535) +* Handle metadata properties in TrxReport by @Youssef1313 in [#5891](https://github.com/microsoft/testfx/pull/5891) +* Obsolete `ITestApplicationLifecycleCallbacks` and add `ITestHostApplicationLifetime` by @Youssef1313 in [#5889](https://github.com/microsoft/testfx/pull/5889) +* Fix TrxReport not including test ignore reason by @Youssef1313 in [#5896](https://github.com/microsoft/testfx/pull/5896) +* Support `--filter-uid` on console by @Youssef1313 in [#6002](https://github.com/microsoft/testfx/pull/6002) +* Support unary not operator in TreeNodeFilter by @Youssef1313 in [#6127](https://github.com/microsoft/testfx/pull/6127) + +### Fixed + +* Fix MTP timeout parsing to use invariant culture instead of current culture by @Copilot in [#5705](https://github.com/microsoft/testfx/pull/5705) +* Add lifecycle callbacks to test host orchestrator by @Youssef1313 in [#5717](https://github.com/microsoft/testfx/pull/5717) +* Avoid potential clash with default TRX file name by @Youssef1313 in [#5939](https://github.com/microsoft/testfx/pull/5939) +* Ensure TestRun id for retries is the same in TRX by @Youssef1313 in [#5945](https://github.com/microsoft/testfx/pull/5945) +* Do not create TestResults dir for help and info options by @Evangelink in [#6020](https://github.com/microsoft/testfx/pull/6020) + +### Housekeeping + +* remove redundant Empty constructor and mark as warning by @SimonCropp in [#5378](https://github.com/microsoft/testfx/pull/5378) +* use char based APIs in TryUnescape by @SimonCropp in [#5608](https://github.com/microsoft/testfx/pull/5608) +* attribute param nullability in TryUnescape by @SimonCropp in [#5609](https://github.com/microsoft/testfx/pull/5609) +* Cleanup TestApplicationResult by @Youssef1313 in [#5615](https://github.com/microsoft/testfx/pull/5615) +* remove redundant fields by @SimonCropp in [#5610](https://github.com/microsoft/testfx/pull/5610) +* Rename RegisterTestFramework adapterFactory parameter to frameworkFactory by @Copilot in [#5691](https://github.com/microsoft/testfx/pull/5691) +* prefer specific scoped variables by @SimonCropp in [#5611](https://github.com/microsoft/testfx/pull/5611) +* \[VSTestBridge] Avoid creating Uri instance for every test case. by @Youssef1313 in [#5743](https://github.com/microsoft/testfx/pull/5743) +* avoid redundant string builder instance in ValidateTestHostEnvironmentVariablesAsync by @SimonCropp in [#5905](https://github.com/microsoft/testfx/pull/5905) +* remove redundant assignment in TryGetOptionArgumentList by @SimonCropp in [#5908](https://github.com/microsoft/testfx/pull/5908) +* remove redundant environment instance in TestHostBuilder by @SimonCropp in [#5914](https://github.com/microsoft/testfx/pull/5914) +* Avoid keeping instance of process in DotnetMuxerLocator by @SimonCropp in [#5911](https://github.com/microsoft/testfx/pull/5911) +* Add ProcessId to IEnvironment by @SimonCropp in [#5913](https://github.com/microsoft/testfx/pull/5913) +* CrashDumpEnvironmentVariableProvider prefixes can be static by @SimonCropp in [#5909](https://github.com/microsoft/testfx/pull/5909) +* remove unused SystemProcessHandler parameter by @SimonCropp in [#5918](https://github.com/microsoft/testfx/pull/5918) +* missing using in AttachDebuggerIfNeeded by @SimonCropp in [#5919](https://github.com/microsoft/testfx/pull/5919) +* redundant null checks in TrxProcessLifetimeHandler by @SimonCropp in [#5960](https://github.com/microsoft/testfx/pull/5960) +* remove redundant BuildConsumerProducersAsync by @SimonCropp in [#5962](https://github.com/microsoft/testfx/pull/5962) +* remove non generic ActionResult by @SimonCropp in [#5959](https://github.com/microsoft/testfx/pull/5959) +* fix nullablity in FromFailedTest by @SimonCropp in [#5971](https://github.com/microsoft/testfx/pull/5971) +* make dicts in NamedPipeBase strong typed by @SimonCropp in [#5973](https://github.com/microsoft/testfx/pull/5973) +* simplify reading installLocation content by @SimonCropp in [#5968](https://github.com/microsoft/testfx/pull/5968) +* update Polyfill and use Process.Kill by @SimonCropp in [#5943](https://github.com/microsoft/testfx/pull/5943) +* Simplify IProcess.MainModule by @SimonCropp in [#5955](https://github.com/microsoft/testfx/pull/5955) +* use ReadExactly from Polyfill by @SimonCropp in [#5972](https://github.com/microsoft/testfx/pull/5972) +* suppress TPEXP warning for solution by @SimonCropp in [#5984](https://github.com/microsoft/testfx/pull/5984) +* use ReadAllTextAsync from polyfill by @SimonCropp in [#5953](https://github.com/microsoft/testfx/pull/5953) +* use TryAdd instead of TryGetValue and Add in Async MessageBus by @SimonCropp in [#5963](https://github.com/microsoft/testfx/pull/5963) +* use XElement.LoadAsync in TrxCompareTool by @SimonCropp in [#5975](https://github.com/microsoft/testfx/pull/5975) +* fix RunSettingsEnvironmentVariableProvider file name by @SimonCropp in [#6007](https://github.com/microsoft/testfx/pull/6007) +* fix nullability of TestProgressState.DiscoveredTests by @SimonCropp in [#6011](https://github.com/microsoft/testfx/pull/6011) +* remove Uid from DiscoveredTests by @SimonCropp in [#6019](https://github.com/microsoft/testfx/pull/6019) +* remove predicate from RetryHelper by @SimonCropp in [#6027](https://github.com/microsoft/testfx/pull/6027) +* update Polyfill and simplify SHA256 usage by @SimonCropp in [#6029](https://github.com/microsoft/testfx/pull/6029) +* simplify TrxCompareTool using an inner type instead of tuples by @SimonCropp in [#6024](https://github.com/microsoft/testfx/pull/6024) +* reduce some linq alloc in AppendTestDiscoverySummary by @SimonCropp in [#6025](https://github.com/microsoft/testfx/pull/6025) +* fix Syncronous typo by @SimonCropp in [#6048](https://github.com/microsoft/testfx/pull/6048) + +### Artifacts + +* Microsoft.Testing.Platform: [1.8.0](https://www.nuget.org/packages/Microsoft.Testing.Platform/1.8.0) +* Microsoft.Testing.Platform.MSBuild: [1.8.0](https://www.nuget.org/packages/Microsoft.Testing.Platform.MSBuild/1.8.0) +* Microsoft.Testing.Extensions.CrashDump: [1.8.0](https://www.nuget.org/packages/Microsoft.Testing.Extensions.CrashDump/1.8.0) +* Microsoft.Testing.Extensions.HangDump: [1.8.0](https://www.nuget.org/packages/Microsoft.Testing.Extensions.HangDump/1.8.0) +* Microsoft.Testing.Extensions.HotReload: [1.8.0](https://www.nuget.org/packages/Microsoft.Testing.Extensions.HotReload/1.8.0) +* Microsoft.Testing.Extensions.Retry: [1.8.0](https://www.nuget.org/packages/Microsoft.Testing.Extensions.Retry/1.8.0) +* Microsoft.Testing.Extensions.Telemetry: [1.8.0](https://www.nuget.org/packages/Microsoft.Testing.Extensions.Telemetry/1.8.0) +* Microsoft.Testing.Extensions.TrxReport: [1.8.0](https://www.nuget.org/packages/Microsoft.Testing.Extensions.TrxReport/1.8.0) +* Microsoft.Testing.Extensions.TrxReport.Abstractions: [1.8.0](https://www.nuget.org/packages/Microsoft.Testing.Extensions.TrxReport.Abstractions/1.8.0) +* Microsoft.Testing.Extensions.VSTestBridge: [1.8.0](https://www.nuget.org/packages/Microsoft.Testing.Extensions.VSTestBridge/1.8.0) +* Microsoft.Testing.Extensions.AzureDevOpsReport [1.0.0-alpha.25379.8](https://www.nuget.org/packages/Microsoft.Testing.Extensions.AzureDevOpsReport/1.0.0-alpha.25379.8) + +## [1.7.3] - 2025-06-17 + +See full log [of v3.9.2...v3.9.3](https://github.com/microsoft/testfx/compare/v3.9.2...v3.9.3) + +### Fixed + +* Simpler fix for dotnet test when using retry by @Youssef1313 in [#5731](https://github.com/microsoft/testfx/pull/5684) + +### Artifacts + +* Microsoft.Testing.Extensions.CrashDump: [1.7.3](https://www.nuget.org/packages/Microsoft.Testing.Extensions.CrashDump/1.7.3) +* Microsoft.Testing.Extensions.HangDump: [1.7.3](https://www.nuget.org/packages/Microsoft.Testing.Extensions.HangDump/1.7.3) +* Microsoft.Testing.Extensions.HotReload: [1.7.3](https://www.nuget.org/packages/Microsoft.Testing.Extensions.HotReload/1.7.3) +* Microsoft.Testing.Extensions.Retry: [1.7.3](https://www.nuget.org/packages/Microsoft.Testing.Extensions.Retry/1.7.3) +* Microsoft.Testing.Extensions.TrxReport: [1.7.3](https://www.nuget.org/packages/Microsoft.Testing.Extensions.TrxReport/1.7.3) +* Microsoft.Testing.Extensions.AzureDevOpsReport [1.0.0-alpha.25317.1](https://www.nuget.org/packages/Microsoft.Testing.Extensions.AzureDevOpsReport/1.0.0-alpha.25317.1) + +## [1.7.2] - 2025-06-10 + +See full log [of v3.9.1...v3.9.2](https://github.com/microsoft/testfx/compare/v3.9.1...v3.9.2) + +### Fixed + +* Allow framework authors to use TestCase.FullyQualifiedName as the TestNodeUid by @Youssef1313 in [#5658](https://github.com/microsoft/testfx/pull/5658) + +### Artifacts + +* Microsoft.Testing.Extensions.CrashDump: [1.7.2](https://www.nuget.org/packages/Microsoft.Testing.Extensions.CrashDump/1.7.2) +* Microsoft.Testing.Extensions.HangDump: [1.7.2](https://www.nuget.org/packages/Microsoft.Testing.Extensions.HangDump/1.7.2) +* Microsoft.Testing.Extensions.HotReload: [1.7.2](https://www.nuget.org/packages/Microsoft.Testing.Extensions.HotReload/1.7.2) +* Microsoft.Testing.Extensions.Retry: [1.7.2](https://www.nuget.org/packages/Microsoft.Testing.Extensions.Retry/1.7.2) +* Microsoft.Testing.Extensions.TrxReport: [1.7.2](https://www.nuget.org/packages/Microsoft.Testing.Extensions.TrxReport/1.7.2) +* Microsoft.Testing.Extensions.AzureDevOpsReport [1.0.0-alpha.25310.6](https://www.nuget.org/packages/Microsoft.Testing.Extensions.AzureDevOpsReport/1.0.0-alpha.25310.6) + +## [1.7.1] - 2025-05-27 + +See full log [of v3.9.0...v3.9.1](https://github.com/microsoft/testfx/compare/v3.9.0...v3.9.1) + +### Fixed + +* VSTestBridge: Handle TestPropertyAttributes.Trait instead of special casing specific properties by @Youssef1313 in [#5644](https://github.com/microsoft/testfx/pull/5644) + +### Artifacts + +* MSTest: [3.9.1](https://www.nuget.org/packages/MSTest/3.9.1) +* MSTest.TestFramework: [3.9.1](https://www.nuget.org/packages/MSTest.TestFramework/3.9.1) +* MSTest.TestAdapter: [3.9.1](https://www.nuget.org/packages/MSTest.TestAdapter/3.9.1) +* MSTest.Analyzers: [3.9.1](https://www.nuget.org/packages/MSTest.Analyzers/3.9.1) +* MSTest.Sdk: [3.9.1](https://www.nuget.org/packages/MSTest.Sdk/3.9.1) +* Microsoft.Testing.Extensions.CrashDump: [1.7.1](https://www.nuget.org/packages/Microsoft.Testing.Extensions.CrashDump/1.7.1) +* Microsoft.Testing.Extensions.HangDump: [1.7.1](https://www.nuget.org/packages/Microsoft.Testing.Extensions.HangDump/1.7.1) +* Microsoft.Testing.Extensions.HotReload: [1.7.1](https://www.nuget.org/packages/Microsoft.Testing.Extensions.HotReload/1.7.1) +* Microsoft.Testing.Extensions.Retry: [1.7.1](https://www.nuget.org/packages/Microsoft.Testing.Extensions.Retry/1.7.1) +* Microsoft.Testing.Extensions.TrxReport: [1.7.1](https://www.nuget.org/packages/Microsoft.Testing.Extensions.TrxReport/1.7.1) +* Microsoft.Testing.Extensions.AzureDevOpsReport [1.0.0-alpha.25277.3](https://www.nuget.org/packages/Microsoft.Testing.Extensions.AzureDevOpsReport/1.0.0-alpha.25277.3) +* MSTest.SourceGeneration: [1.0.0-alpha.25277.3](https://www.nuget.org/packages/MSTest.SourceGeneration/1.0.0-alpha.25277.3) +* MSTest.Engine: [1.0.0-alpha.25277.3](https://www.nuget.org/packages/MSTest.Engine/1.0.0-alpha.25277.3) + +## [1.7.0] - 2025-05-20 + +See full log [of v3.8.3...v3.9.0](https://github.com/microsoft/testfx/compare/v3.8.3...v3.9.0) + +### Added + +* [Source Breaking (only for framework authors)]: Support test artifacts in VS by @Youssef1313 in [#5323](https://github.com/microsoft/testfx/pull/5323) +* Add (alpha) Azure DevOps extension to report errors by @nohwnd in [#5260](https://github.com/microsoft/testfx/pull/5260) +* Add banner for MSTest.Engine by @Youssef1313 in [#5051](https://github.com/microsoft/testfx/pull/5051) +* Use terminal logger for discovery by @nohwnd in [#4907](https://github.com/microsoft/testfx/pull/4907) +* Add RetryContext.FirstRunResults by @Youssef1313 in [#5314](https://github.com/microsoft/testfx/pull/5314) +* VSTestBridge: Add traits as TestMetadataProperty by @Youssef1313 in [#5316](https://github.com/microsoft/testfx/pull/5316) +* Mark APIs not supported in wasi by @Youssef1313 in [#5367](https://github.com/microsoft/testfx/pull/5367) +* Show disk info when hang dump fails by @Youssef1313 in [#5404](https://github.com/microsoft/testfx/pull/5404) +* Implement analyzer for RetryAttribute to be present on test methods by @Youssef1313 in [#5437](https://github.com/microsoft/testfx/pull/5437) +* Add TestMethodIdentifierProperty constructor with arity parameter by @Youssef1313 in [#5528](https://github.com/microsoft/testfx/pull/5528) + +### Fixed + +* Kill testhost if writing hang dump fails by @Youssef1313 in [#5538](https://github.com/microsoft/testfx/pull/5538) +* Simplify generated file name by using DefaultLanguageSourceExtension by @Youssef1313 in [#5026](https://github.com/microsoft/testfx/pull/5026) +* Fix handling of unsupported platforms of CancelKeyPress by @Youssef1313 in [#5038](https://github.com/microsoft/testfx/pull/5038) +* Refactor logic around GetCurrentTestApplicationFullPath by @Youssef1313 in [#5037](https://github.com/microsoft/testfx/pull/5037) +* Enable platform compatibility warnings for android, ios, tvos, and browser by @Youssef1313 in [#5046](https://github.com/microsoft/testfx/pull/5046) +* Improve MSTest.SourceGeneration incrementality by @Youssef1313 in [#5053](https://github.com/microsoft/testfx/pull/5053) +* remove redundant IPlatformOutputDeviceManager by @SimonCropp in [#4848](https://github.com/microsoft/testfx/pull/4848) +* Avoid using unsupported APIs by @Youssef1313 in [#5057](https://github.com/microsoft/testfx/pull/5057) +* Fix binlog base name for .NET Framework tests by @Youssef1313 in [#5102](https://github.com/microsoft/testfx/pull/5102) +* Improve ExecutionContext propagation by @Youssef1313 in [#5156](https://github.com/microsoft/testfx/pull/5156) +* use StringBuilder AppendJoin by @SimonCropp in [#5167](https://github.com/microsoft/testfx/pull/5167) +* Update README.md with MSTest.Sdk information by @stan-sz in [#5214](https://github.com/microsoft/testfx/pull/5214) +* Add assembly name by @nohwnd in [#5235](https://github.com/microsoft/testfx/pull/5235) +* Correct branding as Microsoft.Testing.Platform by @Youssef1313 in [#5240](https://github.com/microsoft/testfx/pull/5240) +* Remove extra space by @nohwnd in [#5238](https://github.com/microsoft/testfx/pull/5238) +* Fix Retry for dotnet test by @Youssef1313 in [#5261](https://github.com/microsoft/testfx/pull/5261) +* Onboard to new dotnet test experience by @Evangelink in [#5111](https://github.com/microsoft/testfx/pull/5111) +* Add InstanceId to communication with dotnet test by @mariam-abdulla in [#5279](https://github.com/microsoft/testfx/pull/5279) +* Add instance id to dotnet test protocol by @mariam-abdulla in [#5287](https://github.com/microsoft/testfx/pull/5287) +* Use FileAccess.Read when reading testconfig.json file by @Youssef1313 in [#5264](https://github.com/microsoft/testfx/pull/5264) +* Fix double empty line by @nohwnd in [#5317](https://github.com/microsoft/testfx/pull/5317) +* Remove SessionUid from FileArtifactProperty by @Youssef1313 in [#5347](https://github.com/microsoft/testfx/pull/5347) +* Fix typo in DotnetTestDataConsumer causes only first artifact to be sent by @Youssef1313 in [#5349](https://github.com/microsoft/testfx/pull/5349) +* fix nullability in GetRepoRoot by @SimonCropp in [#5392](https://github.com/microsoft/testfx/pull/5392) +* remove redundant null check in FormatInnerExceptions by @SimonCropp in [#5397](https://github.com/microsoft/testfx/pull/5397) +* fix nullability of CreateBindCtx by @SimonCropp in [#5385](https://github.com/microsoft/testfx/pull/5385) +* remove redundant control flow statements by @SimonCropp in [#5403](https://github.com/microsoft/testfx/pull/5403) +* fix nullability of InvokeTestingPlatformTask _outputFileName by @SimonCropp in [#5394](https://github.com/microsoft/testfx/pull/5394) +* fix nullability of argument in GetProcessExitCodeAsync_IgnoreExitCodes by @SimonCropp in [#5386](https://github.com/microsoft/testfx/pull/5386) +* remove redundant null check for OpenBaseKey return by @SimonCropp in [#5395](https://github.com/microsoft/testfx/pull/5395) +* remove redundant null check in GetStringFromIndexOrDefault by @SimonCropp in [#5396](https://github.com/microsoft/testfx/pull/5396) +* fix nullability in FileLoggerProvider by @SimonCropp in [#5398](https://github.com/microsoft/testfx/pull/5398) +* remove un-used TestNodeProcessor by @SimonCropp in [#5430](https://github.com/microsoft/testfx/pull/5430) +* Rename MTP entrypoint to MicrosoftTestingPlatformEntryPoint by @Youssef1313 in [#5423](https://github.com/microsoft/testfx/pull/5423) +* use null propagation and mark as warning in editorconfig by @SimonCropp in [#5383](https://github.com/microsoft/testfx/pull/5383) +* FindNode cant return null by @SimonCropp in [#5448](https://github.com/microsoft/testfx/pull/5448) +* remove un-used methods in UnicodeCharacterUtilities by @SimonCropp in [#5444](https://github.com/microsoft/testfx/pull/5444) +* remove ServerLogMessageInMemoryStore by @SimonCropp in [#5456](https://github.com/microsoft/testfx/pull/5456) +* Remove some redundant casts and mark as a warning for rider and R# by @SimonCropp in [#5459](https://github.com/microsoft/testfx/pull/5459) +* Use GetFileNameWithoutExtension for crashdump file name to be consistent with hangdump by @Youssef1313 in [#5454](https://github.com/microsoft/testfx/pull/5454) +* Remove dead --internal-vstest-adapter by @Youssef1313 in [#5450](https://github.com/microsoft/testfx/pull/5450) +* Consistent command-line options provider properties by @Youssef1313 in [#5452](https://github.com/microsoft/testfx/pull/5452) +* Update Fakes dependency by @stan-sz in [#5482](https://github.com/microsoft/testfx/pull/5482) +* Use PlatformVersion.Version as the server version by @Youssef1313 in [#5486](https://github.com/microsoft/testfx/pull/5486) +* Handle DebugOrTraceTrxMessage in TrxReportEngine by @Youssef1313 in [#5510](https://github.com/microsoft/testfx/pull/5510) +* Few improvements to AzDO extension by @Youssef1313 in [#5513](https://github.com/microsoft/testfx/pull/5513) +* VSTestBridge+MSTest: Use TestMethodIdentifierProperty and stop sending VSTest-specifics by @Youssef1313 in [#5409](https://github.com/microsoft/testfx/pull/5409) +* Add vstest.TestCase.CodeFilePath and vstest.TestCase.LineNumber by @Youssef1313 in [#5539](https://github.com/microsoft/testfx/pull/5539) + +### Artifacts + +* MSTest: [3.9.0](https://www.nuget.org/packages/MSTest/3.9.0) +* MSTest.TestFramework: [3.9.0](https://www.nuget.org/packages/MSTest.TestFramework/3.9.0) +* MSTest.TestAdapter: [3.9.0](https://www.nuget.org/packages/MSTest.TestAdapter/3.9.0) +* MSTest.Analyzers: [3.9.0](https://www.nuget.org/packages/MSTest.Analyzers/3.9.0) +* MSTest.Sdk: [3.9.0](https://www.nuget.org/packages/MSTest.Sdk/3.9.0) +* Microsoft.Testing.Extensions.CrashDump: [1.7.0](https://www.nuget.org/packages/Microsoft.Testing.Extensions.CrashDump/1.7.0) +* Microsoft.Testing.Extensions.HangDump: [1.7.0](https://www.nuget.org/packages/Microsoft.Testing.Extensions.HangDump/1.7.0) +* Microsoft.Testing.Extensions.HotReload: [1.7.0](https://www.nuget.org/packages/Microsoft.Testing.Extensions.HotReload/1.7.0) +* Microsoft.Testing.Extensions.Retry: [1.7.0](https://www.nuget.org/packages/Microsoft.Testing.Extensions.Retry/1.7.0) +* Microsoft.Testing.Extensions.TrxReport: [1.7.0](https://www.nuget.org/packages/Microsoft.Testing.Extensions.TrxReport/1.7.0) +* Microsoft.Testing.Extensions.AzureDevOpsReport [1.0.0-alpha.25256.6](https://www.nuget.org/packages/Microsoft.Testing.Extensions.AzureDevOpsReport/1.0.0-alpha.25256.6) +* MSTest.SourceGeneration: [1.0.0-alpha.25256.6](https://www.nuget.org/packages/MSTest.SourceGeneration/1.0.0-alpha.25256.6) +* MSTest.Engine: [1.0.0-alpha.25256.6](https://www.nuget.org/packages/MSTest.Engine/1.0.0-alpha.25256.6) + ## [1.6.3] - 2025-03-17 -See full log [here](https://github.com/microsoft/testfx/compare/v3.8.2...v3.8.3) +See full log [of v3.8.2...v3.8.3](https://github.com/microsoft/testfx/compare/v3.8.2...v3.8.3) ### Fixed @@ -35,7 +361,7 @@ See full log [here](https://github.com/microsoft/testfx/compare/v3.8.2...v3.8.3) ## [1.6.2] - 2025-02-19 -See full log [here](https://github.com/microsoft/testfx/compare/v3.8.1...v3.8.2) +See full log [of v3.8.1...v3.8.2](https://github.com/microsoft/testfx/compare/v3.8.1...v3.8.2) ### Fixed @@ -56,7 +382,7 @@ See full log [here](https://github.com/microsoft/testfx/compare/v3.8.1...v3.8.2) ## [1.6.1] - 2025-02-18 -See full log [here](https://github.com/microsoft/testfx/compare/v3.8.0...v3.8.1) +See full log [of v3.8.0...v3.8.1](https://github.com/microsoft/testfx/compare/v3.8.0...v3.8.1) ### Fixed @@ -79,7 +405,7 @@ See full log [here](https://github.com/microsoft/testfx/compare/v3.8.0...v3.8.1) ## [1.6.0] - 2025-02-12 -See full log [here](https://github.com/microsoft/testfx/compare/v3.7.3...v3.8.0) +See full log [of v3.7.3...v3.8.0](https://github.com/microsoft/testfx/compare/v3.7.3...v3.8.0) ### Added @@ -117,7 +443,7 @@ See full log [here](https://github.com/microsoft/testfx/compare/v3.7.3...v3.8.0) ## [1.5.3] - 2025-01-27 -See full log [here](https://github.com/microsoft/testfx/compare/v3.7.2...v3.7.3) +See full log [of v3.7.2...v3.7.3](https://github.com/microsoft/testfx/compare/v3.7.2...v3.7.3) ### Fixed @@ -138,7 +464,7 @@ See full log [here](https://github.com/microsoft/testfx/compare/v3.7.2...v3.7.3) ## [1.5.2] - 2025-01-21 -See full log [here](https://github.com/microsoft/testfx/compare/v3.7.1...v3.7.2) +See full log [of v3.7.1...v3.7.2](https://github.com/microsoft/testfx/compare/v3.7.1...v3.7.2) ### Fixed @@ -159,7 +485,7 @@ See full log [here](https://github.com/microsoft/testfx/compare/v3.7.1...v3.7.2) ## [1.5.1] - 2025-01-13 -See full log [here](https://github.com/microsoft/testfx/compare/v3.7.0...v3.7.1) +See full log [of v3.7.0...v3.7.1](https://github.com/microsoft/testfx/compare/v3.7.0...v3.7.1) ### Fixed @@ -181,7 +507,7 @@ See full log [here](https://github.com/microsoft/testfx/compare/v3.7.0...v3.7.1) ## [1.5.0] - 2024-12-20 -See full log [here](https://github.com/microsoft/testfx/compare/v1.4.3...v1.5.0) +See full log [of v1.4.3...v1.5.0](https://github.com/microsoft/testfx/compare/v1.4.3...v1.5.0) ### Added @@ -236,7 +562,7 @@ See full log [here](https://github.com/microsoft/testfx/compare/v1.4.3...v1.5.0) ## [1.4.3] - 2024-11-12 -See full log [here](https://github.com/microsoft/testanywhere/compare/v1.4.2...v1.4.3) +See full log [of v1.4.2...v1.4.3](https://github.com/microsoft/testanywhere/compare/v1.4.2...v1.4.3) ### Fixed @@ -259,7 +585,7 @@ See full log [here](https://github.com/microsoft/testanywhere/compare/v1.4.2...v ## [1.4.2] - 2024-10-31 -See full log [here](https://github.com/microsoft/testanywhere/compare/v1.4.1...v1.4.2) +See full log [of v1.4.1...v1.4.2](https://github.com/microsoft/testanywhere/compare/v1.4.1...v1.4.2) ### Fixed @@ -282,7 +608,7 @@ See full log [here](https://github.com/microsoft/testanywhere/compare/v1.4.1...v ## [1.4.1] - 2024-10-03 -See full log [here](https://github.com/microsoft/testanywhere/compare/v1.4.0...v1.4.1) +See full log [of v1.4.0...v1.4.1](https://github.com/microsoft/testanywhere/compare/v1.4.0...v1.4.1) ### Fixed @@ -305,7 +631,7 @@ See full log [here](https://github.com/microsoft/testanywhere/compare/v1.4.0...v ## [1.4.0] - 2024-09-11 -See full log [here](https://github.com/microsoft/testanywhere/compare/v1.3.2...v1.4.0) +See full log [of v1.3.2...v1.4.0](https://github.com/microsoft/testanywhere/compare/v1.3.2...v1.4.0) ### Added @@ -377,7 +703,7 @@ See full log [here](https://github.com/microsoft/testanywhere/compare/v1.3.2...v ## [1.3.2] - 2024-08-05 -See full log [here](https://github.com/microsoft/testanywhere/compare/v1.3.1...v1.3.2) +See full log [of v1.3.1...v1.3.2](https://github.com/microsoft/testanywhere/compare/v1.3.1...v1.3.2) ### Fixed @@ -401,7 +727,7 @@ See full log [here](https://github.com/microsoft/testanywhere/compare/v1.3.1...v ## [1.3.1] - 2024-07-15 -See full log [here](https://github.com/microsoft/testanywhere/compare/v1.2.1...v1.3.1) +See full log [of v1.2.1...v1.3.1](https://github.com/microsoft/testanywhere/compare/v1.2.1...v1.3.1) ### Added diff --git a/docs/Changelog.md b/docs/Changelog.md index 24e4e4da1b..f831a96576 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -4,9 +4,360 @@ 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.1.0/) +## [3.10.5] - 2025-09-30 + +See full log [of v3.10.4...v3.10.5](https://github.com/microsoft/testfx/compare/v3.10.4...v3.10.5) + +### Fixed + +* Include TestFramework.Extensions.dll in TestAdapter NuGet package for .NET Framework by @Youssef1313 in [#6625](https://github.com/microsoft/testfx/pull/6625) + +### Artifacts + +* MSTest: [3.10.5](https://www.nuget.org/packages/MSTest/3.10.5) +* MSTest.TestFramework: [3.10.5](https://www.nuget.org/packages/MSTest.TestFramework/3.10.5) +* MSTest.TestAdapter: [3.10.5](https://www.nuget.org/packages/MSTest.TestAdapter/3.10.5) +* MSTest.Analyzers: [3.10.5](https://www.nuget.org/packages/MSTest.Analyzers/3.10.5) +* MSTest.Sdk: [3.10.5](https://www.nuget.org/packages/MSTest.Sdk/3.10.5) +* MSTest.SourceGeneration: [1.0.0-alpha.25480.6](https://www.nuget.org/packages/MSTest.SourceGeneration/1.0.0-alpha.25480.6) +* MSTest.Engine: [1.0.0-alpha.25480.6](https://www.nuget.org/packages/MSTest.Engine/1.0.0-alpha.25480.6) + +## [3.10.4] - 2025-09-02 + +See full log [of v3.10.3...v3.10.4](https://github.com/microsoft/testfx/compare/v3.10.3...v3.10.4) + +### Fixed + +* Synchronize _testContextMessageStringBuilder by @Youssef1313 in [#6459](https://github.com/microsoft/testfx/pull/6459) +* Fix MSTEST0001 (parallelization analyzer) to work with VSTest by @Youssef1313 in [#6480](https://github.com/microsoft/testfx/pull/6480) +* Fix StringAssertToAssertFixer to swap StringComparison and message arguments by @Youssef1313 in [#6481](https://github.com/microsoft/testfx/pull/6481) +* Fix MSTEST0002 codefix to not change modifier order by @Youssef1313 in [#6479](https://github.com/microsoft/testfx/pull/6479) + +### Artifacts + +* MSTest: [3.10.4](https://www.nuget.org/packages/MSTest/3.10.4) +* MSTest.TestFramework: [3.10.4](https://www.nuget.org/packages/MSTest.TestFramework/3.10.4) +* MSTest.TestAdapter: [3.10.4](https://www.nuget.org/packages/MSTest.TestAdapter/3.10.4) +* MSTest.Analyzers: [3.10.4](https://www.nuget.org/packages/MSTest.Analyzers/3.10.4) +* MSTest.Sdk: [3.10.4](https://www.nuget.org/packages/MSTest.Sdk/3.10.4) +* MSTest.SourceGeneration: [1.0.0-alpha.25452.8](https://www.nuget.org/packages/MSTest.SourceGeneration/1.0.0-alpha.25452.8) +* MSTest.Engine: [1.0.0-alpha.25452.8](https://www.nuget.org/packages/MSTest.Engine/1.0.0-alpha.25452.8) + +## [3.10.3] - 2025-08-26 + +See full log [of v3.10.2...v3.10.3](https://github.com/microsoft/testfx/compare/v3.10.2...v3.10.3) + +### Fixed + +* No change, released to keep version aligned with Microsoft.Testing.Platform. + +### Artifacts + +* MSTest: [3.10.3](https://www.nuget.org/packages/MSTest/3.10.3) +* MSTest.TestFramework: [3.10.3](https://www.nuget.org/packages/MSTest.TestFramework/3.10.3) +* MSTest.TestAdapter: [3.10.3](https://www.nuget.org/packages/MSTest.TestAdapter/3.10.3) +* MSTest.Analyzers: [3.10.3](https://www.nuget.org/packages/MSTest.Analyzers/3.10.3) +* MSTest.Sdk: [3.10.3](https://www.nuget.org/packages/MSTest.Sdk/3.10.3) +* MSTest.SourceGeneration: [1.0.0-alpha.25425.9](https://www.nuget.org/packages/MSTest.SourceGeneration/1.0.0-alpha.25425.9) +* MSTest.Engine: [1.0.0-alpha.25425.9](https://www.nuget.org/packages/MSTest.Engine/1.0.0-alpha.25425.9) + +## [3.10.2] - 2025-08-12 + +See full log [of v3.10.1...v3.10.2](https://github.com/microsoft/testfx/compare/v3.10.1...v3.10.2) + +### Fixed + +* Revert Assert.That obsoletion by @Youssef1313 in [#6323](https://github.com/microsoft/testfx/pull/6323) + +### Artifacts + +* MSTest: [3.10.2](https://www.nuget.org/packages/MSTest/3.10.2) +* MSTest.TestFramework: [3.10.2](https://www.nuget.org/packages/MSTest.TestFramework/3.10.2) +* MSTest.TestAdapter: [3.10.2](https://www.nuget.org/packages/MSTest.TestAdapter/3.10.2) +* MSTest.Analyzers: [3.10.2](https://www.nuget.org/packages/MSTest.Analyzers/3.10.2) +* MSTest.Sdk: [3.10.2](https://www.nuget.org/packages/MSTest.Sdk/3.10.2) +* MSTest.SourceGeneration: [1.0.0-alpha.25411.2](https://www.nuget.org/packages/MSTest.SourceGeneration/1.0.0-alpha.25411.2) +* MSTest.Engine: [1.0.0-alpha.25411.2](https://www.nuget.org/packages/MSTest.Engine/1.0.0-alpha.25411.2) + +## [3.10.1] - 2025-08-05 + +See full log [of v3.10.0...v3.10.1](https://github.com/microsoft/testfx/compare/v3.10.0...v3.10.1) + +### Fixed + +* Add compat overload for GetPipeName by @Youssef1313 in [#6227](https://github.com/microsoft/testfx/pull/6227) +* Fix race in TestContextImplementation by @Youssef1313 in [#6249](https://github.com/microsoft/testfx/pull/6249) +* Fix codefix of analyzer for flowing cancellation token by @Copilot in [#6239](https://github.com/microsoft/testfx/pull/6239) +* Don't return null types from AssemblyEnumerator when ReflectionTypeLoadException is encountered by @Youssef1313 in [#6276](https://github.com/microsoft/testfx/pull/6276) +* Fix analyzer false positives for collection asserts by @Youssef1313 in [#6300](https://github.com/microsoft/testfx/pull/6300) + +### Artifacts + +* MSTest: [3.10.1](https://www.nuget.org/packages/MSTest/3.10.1) +* MSTest.TestFramework: [3.10.1](https://www.nuget.org/packages/MSTest.TestFramework/3.10.1) +* MSTest.TestAdapter: [3.10.1](https://www.nuget.org/packages/MSTest.TestAdapter/3.10.1) +* MSTest.Analyzers: [3.10.1](https://www.nuget.org/packages/MSTest.Analyzers/3.10.1) +* MSTest.Sdk: [3.10.1](https://www.nuget.org/packages/MSTest.Sdk/3.10.1) +* MSTest.SourceGeneration: [1.0.0-alpha.25405.1](https://www.nuget.org/packages/MSTest.SourceGeneration/1.0.0-alpha.25405.1) +* MSTest.Engine: [1.0.0-alpha.25405.1](https://www.nuget.org/packages/MSTest.Engine/1.0.0-alpha.25405.1) + +## [3.10.0] - 2025-07-29 + +See full log [of v3.9.3...v3.10.0](https://github.com/microsoft/testfx/compare/v3.9.3...v3.10.0) + +### Added + +* Inherit OwnerAttribute and PriorityAttribute from TestPropertyAttribute by @Youssef1313 in [#5591](https://github.com/microsoft/testfx/pull/5591) +* Better handling of MSTest.TestAdapter under .NET Standard by @Youssef1313 in [#5647](https://github.com/microsoft/testfx/pull/5647) +* Add implicit using even when not using MSTest.Sdk by @Youssef1313 in [#5589](https://github.com/microsoft/testfx/pull/5589) +* Allow serialization of DateOnly/TimeOnly for parameterized tests by @Youssef1313 in [#5676](https://github.com/microsoft/testfx/pull/5676) +* Implement analyzer/codefix to move from DataTestMethodAttribute to TestMethodAttribute by @Copilot in [#5706](https://github.com/microsoft/testfx/pull/5706) +* Error if framework/adapter versions are mismatched at runtime by @Youssef1313 in [#5703](https://github.com/microsoft/testfx/pull/5703) +* Add predicate-based overloads for Assert.ContainsSingle API by @Copilot in [#5767](https://github.com/microsoft/testfx/pull/5767) +* Add Assert.IsInRange API with three overloads by @Copilot in [#5765](https://github.com/microsoft/testfx/pull/5765) +* Add comparison Assert APIs (IsGreaterThan, IsLessThan, IsPositive, IsNegative) by @Copilot in [#5790](https://github.com/microsoft/testfx/pull/5790) +* Add analyzer to suggest using cooperative cancellation for timeout by @Copilot in [#5786](https://github.com/microsoft/testfx/pull/5786) +* Deprecates `That` property and suggest `Instance` instead by @Evangelink in [#5811](https://github.com/microsoft/testfx/pull/5811) +* Improve error message for all Assert.Contains.cs APIs by @Copilot in [#5793](https://github.com/microsoft/testfx/pull/5793) +* Add Assert StartsWith/DoesNotStartWith, EndsWith/DoesNotEndsWith, MatchesRegex/DoesNotMatchRegex by @Evangelink in [#5768](https://github.com/microsoft/testfx/pull/5768) +* Obsolete Assert.Equals and add obsolete Assert.ReferenceEquals by @Copilot in [#5815](https://github.com/microsoft/testfx/pull/5815) +* Bump AvoidExpectedExceptionAttributeAnalyzer to warning by @Youssef1313 in [#5864](https://github.com/microsoft/testfx/pull/5864) +* Bump UseNewerAssertThrowsAnalyzer to warning by @Youssef1313 in [#5863](https://github.com/microsoft/testfx/pull/5863) +* Add analyzer and code fix to migrate from StringAssert to Assert APIs by @Copilot in [#5792](https://github.com/microsoft/testfx/pull/5792) +* Improve DataRow type mismatch error messages with descriptive parameter information by @Copilot in [#5819](https://github.com/microsoft/testfx/pull/5819) +* Inherit DescriptionAttribute from TestPropertyAttribute by @Youssef1313 in [#6004](https://github.com/microsoft/testfx/pull/6004) +* Add CIConditionAttribute to support CI-specific test execution control by @Copilot in [#5797](https://github.com/microsoft/testfx/pull/5797) +* Obsolete CssIteration and CssProjectStructure attributes for v3.10 by @Copilot in [#5981](https://github.com/microsoft/testfx/pull/5981) +* Support passing arguments to DynamicData methods by @Youssef1313 in [#5892](https://github.com/microsoft/testfx/pull/5892) +* Add TestCategories property to ITestDataRow for per-test-case categorization by @Copilot in [#5795](https://github.com/microsoft/testfx/pull/5795) +* Add diagnostic suppressor for IDE0060 warnings on TestContext parameters in MSTest fixture methods by @Copilot in [#6055](https://github.com/microsoft/testfx/pull/6055) +* Add analyzer for incorrect TestContext property usage in fixture methods by @Copilot in [#5990](https://github.com/microsoft/testfx/pull/5990) +* Update UseProperAssertMethodsAnalyzer to handle more use cases by @Copilot in [#6058](https://github.com/microsoft/testfx/pull/6058) +* Add analyzer to flow TestContext.CTS.Token by @Youssef1313 in [#6126](https://github.com/microsoft/testfx/pull/6126) +* Obsolete `TestTimeout` by @Evangelink in [#6144](https://github.com/microsoft/testfx/pull/6144) +* Make ConditionBaseAttribute.IgnoreMessage settable by @Evangelink in [#6150](https://github.com/microsoft/testfx/pull/6150) +* Add GlobalTestInitializeAttribute and GlobalTestCleanupAttribute by @Youssef1313 in [#6132](https://github.com/microsoft/testfx/pull/6132) + +### Fixed + +* OSConditionAttribute: Make ignore message depend on ConditionMode by @Youssef1313 in [#5587](https://github.com/microsoft/testfx/pull/5587) +* Fix MSTEST0004 analyzer to recognize TestClass-derived attributes by @Copilot in [#5600](https://github.com/microsoft/testfx/pull/5600) +* Fix MSTEST0005 false positive when using null-checking with TestContext constructor parameter by @Copilot in [#5601](https://github.com/microsoft/testfx/pull/5601) +* Fix async void analyzer to detect StringAssert and CollectionAssert by @Copilot in [#5650](https://github.com/microsoft/testfx/pull/5650) +* Fix leak in TestRunCancellationToken by @Youssef1313 in [#5730](https://github.com/microsoft/testfx/pull/5730) +* Fix TraceListenerManager thread safety issue by @Youssef1313 in [#5750](https://github.com/microsoft/testfx/pull/5750) +* Store the actual data to instead of deserializing by @Youssef1313 in [#5778](https://github.com/microsoft/testfx/pull/5778) +* Fix STA regression when async method completes asynchronously between sync methods by @Youssef1313 in [#5922](https://github.com/microsoft/testfx/pull/5922) +* Call GetDisplayName with the right arguments when tuples are used by @Youssef1313 in [#5929](https://github.com/microsoft/testfx/pull/5929) +* Fix MSTEST0020 codefix placing code in static constructors by @Copilot in [#5935](https://github.com/microsoft/testfx/pull/5935) +* Fix MSTEST0020 codefix constructor added in place of the TestInitialize method by @Copilot in [#5944](https://github.com/microsoft/testfx/pull/5944) +* Fix TypeContainingTestMethodShouldBeATestClassAnalyzer to handle structs with TestMethod by @Copilot in [#5818](https://github.com/microsoft/testfx/pull/5818) +* MSTest configuration improvement by @Youssef1313 in [#6078](https://github.com/microsoft/testfx/pull/6078) +* Fix nullability suppressor for TestContext when explicit constructor is declared by @Youssef1313 in [#6139](https://github.com/microsoft/testfx/pull/6139) +* Fix codefix behavior for partial classes with TestCleanup and Dispose in different parts by @Copilot in [#6164](https://github.com/microsoft/testfx/pull/6164) +* Add missing `Func` overload to ThrowsExactly by @Youssef1313 in [#6195](https://github.com/microsoft/testfx/pull/6195) + +### Housekeeping + +* Simplify reflection by @Youssef1313 in [#4702](https://github.com/microsoft/testfx/pull/4702) +* remove redudnant cast to IReadOnlyList and add a not null ckeck in AssemblyEnumeratorTests by @SimonCropp in [#5402](https://github.com/microsoft/testfx/pull/5402) +* use more collection expressions and mark as error in editorconfig by @SimonCropp in [#5377](https://github.com/microsoft/testfx/pull/5377) +* Add `copilot-instructions.md` by @Evangelink in [#5798](https://github.com/microsoft/testfx/pull/5798) +* Move files from adapter to platform services project by @Evangelink in [#5713](https://github.com/microsoft/testfx/pull/5713) +* Use ConfigureAwait(false) by @Youssef1313 in [#5719](https://github.com/microsoft/testfx/pull/5719) +* Add Dependabot configuration for .NET SDK updates by @JamieMagee in [#5861](https://github.com/microsoft/testfx/pull/5861) +* projects should inherit defined constants by @SimonCropp in [#5893](https://github.com/microsoft/testfx/pull/5893) +* check logging level in BridgedTraceLogger by @SimonCropp in [#5904](https://github.com/microsoft/testfx/pull/5904) +* fix nullability of CreateBindCtx by @SimonCropp in [#5907](https://github.com/microsoft/testfx/pull/5907) +* remove-zero-width-spaces-from-banned-symbols by @SimonCropp in [#5912](https://github.com/microsoft/testfx/pull/5912) +* use some string interpolation by @SimonCropp in [#5915](https://github.com/microsoft/testfx/pull/5915) +* use char based overloads by @SimonCropp in [#5916](https://github.com/microsoft/testfx/pull/5916) +* use some computed properties by @SimonCropp in [#5961](https://github.com/microsoft/testfx/pull/5961) +* remove some dead consts in EngineConstants by @SimonCropp in [#5967](https://github.com/microsoft/testfx/pull/5967) +* redundant null check in TryAddSearchDirectoriesSpecifiedInRunSettingsToAssemblyResolver by @SimonCropp in [#5966](https://github.com/microsoft/testfx/pull/5966) +* avoid dictionary lookups using TryAdd by @SimonCropp in [#5983](https://github.com/microsoft/testfx/pull/5983) +* remove redundnant escapedClassFullName in AppendAssemblyTestNodeBuilderContent by @SimonCropp in [#6012](https://github.com/microsoft/testfx/pull/6012) +* use the in built StringBuilder append char count by @SimonCropp in [#6013](https://github.com/microsoft/testfx/pull/6013) +* remove some un-used variables by @SimonCropp in [#6010](https://github.com/microsoft/testfx/pull/6010) +* Dont generate documentation for tests and samples by @SimonCropp in [#5441](https://github.com/microsoft/testfx/pull/5441) +* remove some empty type declarations by @SimonCropp in [#6041](https://github.com/microsoft/testfx/pull/6041) +* simplify IsNaN checks by @SimonCropp in [#6043](https://github.com/microsoft/testfx/pull/6043) +* use some exception filters by @SimonCropp in [#6049](https://github.com/microsoft/testfx/pull/6049) +* remove some un-used polyfills by @SimonCropp in [#6042](https://github.com/microsoft/testfx/pull/6042) +* fix Coxtext typo by @SimonCropp in [#6051](https://github.com/microsoft/testfx/pull/6051) +* Fix incorrect comments by @martincostello in [#6189](https://github.com/microsoft/testfx/pull/6189) + +### New Contributors + +* @JamieMagee made their first contribution in [#5861](https://github.com/microsoft/testfx/pull/5861) +* @martincostello made their first contribution in [#6198](https://github.com/microsoft/testfx/pull/6189) + +### Artifacts + +* MSTest: [3.10.0](https://www.nuget.org/packages/MSTest/3.10.0) +* MSTest.TestFramework: [3.10.0](https://www.nuget.org/packages/MSTest.TestFramework/3.10.0) +* MSTest.TestAdapter: [3.10.0](https://www.nuget.org/packages/MSTest.TestAdapter/3.10.0) +* MSTest.Analyzers: [3.10.0](https://www.nuget.org/packages/MSTest.Analyzers/3.10.0) +* MSTest.Sdk: [3.10.0](https://www.nuget.org/packages/MSTest.Sdk/3.10.0) +* MSTest.SourceGeneration: [1.0.0-alpha.25379.8](https://www.nuget.org/packages/MSTest.SourceGeneration/1.0.0-alpha.25379.8) +* MSTest.Engine: [1.0.0-alpha.25379.8](https://www.nuget.org/packages/MSTest.Engine/1.0.0-alpha.25379.8) + +## [3.9.3] - 2025-06-17 + +See full log [of v3.9.2...v3.9.3](https://github.com/microsoft/testfx/compare/v3.9.2...v3.9.3) + +### Fixed + +* No change, released to keep version aligned with Microsoft.Testing.Platform. + +### Artifacts + +* MSTest: [3.9.3](https://www.nuget.org/packages/MSTest/3.9.3) +* MSTest.TestFramework: [3.9.3](https://www.nuget.org/packages/MSTest.TestFramework/3.9.3) +* MSTest.TestAdapter: [3.9.3](https://www.nuget.org/packages/MSTest.TestAdapter/3.9.3) +* MSTest.Analyzers: [3.9.3](https://www.nuget.org/packages/MSTest.Analyzers/3.9.3) +* MSTest.Sdk: [3.9.3](https://www.nuget.org/packages/MSTest.Sdk/3.9.3) +* Microsoft.Testing.Extensions.CrashDump: [1.7.3](https://www.nuget.org/packages/Microsoft.Testing.Extensions.CrashDump/1.7.3) +* Microsoft.Testing.Extensions.HangDump: [1.7.3](https://www.nuget.org/packages/Microsoft.Testing.Extensions.HangDump/1.7.3) +* Microsoft.Testing.Extensions.HotReload: [1.7.3](https://www.nuget.org/packages/Microsoft.Testing.Extensions.HotReload/1.7.3) +* Microsoft.Testing.Extensions.Retry: [1.7.3](https://www.nuget.org/packages/Microsoft.Testing.Extensions.Retry/1.7.3) +* Microsoft.Testing.Extensions.TrxReport: [1.7.3](https://www.nuget.org/packages/Microsoft.Testing.Extensions.TrxReport/1.7.3) +* Microsoft.Testing.Extensions.AzureDevOpsReport [1.0.0-alpha.25317.1](https://www.nuget.org/packages/Microsoft.Testing.Extensions.AzureDevOpsReport/1.0.0-alpha.25317.1) +* MSTest.SourceGeneration: [1.0.0-alpha.25317.1](https://www.nuget.org/packages/MSTest.SourceGeneration/1.0.0-alpha.25317.1) +* MSTest.Engine: [1.0.0-alpha.25317.1](https://www.nuget.org/packages/MSTest.Engine/1.0.0-alpha.25317.1) + +## [3.9.2] - 2025-06-10 + +See full log [of v3.9.1...v3.9.2](https://github.com/microsoft/testfx/compare/v3.9.1...v3.9.2) + +### Fixed + +* Fix MSTEST0042 (duplicate data row) false positive with Zero/NegativeZero by @Youssef1313 in [#5684](https://github.com/microsoft/testfx/pull/5684) +* Ensure TestMethodAttribute.Execute is run on the correct execution context by @Youssef1313 in [#5688](https://github.com/microsoft/testfx/pull/5688) +* Avoid loading System.Threading.Tasks.Extensions when not needed by @Youssef1313 in [#5694](https://github.com/microsoft/testfx/pull/5694) +* Fix UseAsync property in TestMethodAttribute derived classes to use type checks by @Youssef1313 and @Copilot in [#5708](https://github.com/microsoft/testfx/pull/5708) +* Fix UnitTestRunner leaking some test class instances by @Youssef1313 in [#5715](https://github.com/microsoft/testfx/pull/5715) + +### Artifacts + +* MSTest: [3.9.2](https://www.nuget.org/packages/MSTest/3.9.2) +* MSTest.TestFramework: [3.9.2](https://www.nuget.org/packages/MSTest.TestFramework/3.9.2) +* MSTest.TestAdapter: [3.9.2](https://www.nuget.org/packages/MSTest.TestAdapter/3.9.2) +* MSTest.Analyzers: [3.9.2](https://www.nuget.org/packages/MSTest.Analyzers/3.9.2) +* MSTest.Sdk: [3.9.2](https://www.nuget.org/packages/MSTest.Sdk/3.9.2) +* Microsoft.Testing.Extensions.CrashDump: [1.7.2](https://www.nuget.org/packages/Microsoft.Testing.Extensions.CrashDump/1.7.2) +* Microsoft.Testing.Extensions.HangDump: [1.7.2](https://www.nuget.org/packages/Microsoft.Testing.Extensions.HangDump/1.7.2) +* Microsoft.Testing.Extensions.HotReload: [1.7.2](https://www.nuget.org/packages/Microsoft.Testing.Extensions.HotReload/1.7.2) +* Microsoft.Testing.Extensions.Retry: [1.7.2](https://www.nuget.org/packages/Microsoft.Testing.Extensions.Retry/1.7.2) +* Microsoft.Testing.Extensions.TrxReport: [1.7.2](https://www.nuget.org/packages/Microsoft.Testing.Extensions.TrxReport/1.7.2) +* Microsoft.Testing.Extensions.AzureDevOpsReport [1.0.0-alpha.25310.6](https://www.nuget.org/packages/Microsoft.Testing.Extensions.AzureDevOpsReport/1.0.0-alpha.25310.6) +* MSTest.SourceGeneration: [1.0.0-alpha.25310.6](https://www.nuget.org/packages/MSTest.SourceGeneration/1.0.0-alpha.25310.6) +* MSTest.Engine: [1.0.0-alpha.25310.6](https://www.nuget.org/packages/MSTest.Engine/1.0.0-alpha.25310.6) + +## [3.9.1] - 2025-05-27 + +See full log [of v3.9.0...v3.9.1](https://github.com/microsoft/testfx/compare/v3.9.0...v3.9.1) + +### Fixed + +* Make ConditionBaseAttribute.Mode public by @Youssef1313 in [#5581](https://github.com/microsoft/testfx/pull/5581) +* Add missing overload for Assert.Throws by @Youssef1313 in [#5619](https://github.com/microsoft/testfx/pull/5619) +* Fix System.MissingMethodException for KeyValuePair Deconstruction by @Youssef1313 in [#5633](https://github.com/microsoft/testfx/pull/5633) +* Run the whole ExecuteInternal logic under the right execution context by @Youssef1313 in [#5636](https://github.com/microsoft/testfx/pull/5636) + +### Artifacts + +* MSTest: [3.9.1](https://www.nuget.org/packages/MSTest/3.9.1) +* MSTest.TestFramework: [3.9.1](https://www.nuget.org/packages/MSTest.TestFramework/3.9.1) +* MSTest.TestAdapter: [3.9.1](https://www.nuget.org/packages/MSTest.TestAdapter/3.9.1) +* MSTest.Analyzers: [3.9.1](https://www.nuget.org/packages/MSTest.Analyzers/3.9.1) +* MSTest.Sdk: [3.9.1](https://www.nuget.org/packages/MSTest.Sdk/3.9.1) +* Microsoft.Testing.Extensions.CrashDump: [1.7.1](https://www.nuget.org/packages/Microsoft.Testing.Extensions.CrashDump/1.7.1) +* Microsoft.Testing.Extensions.HangDump: [1.7.1](https://www.nuget.org/packages/Microsoft.Testing.Extensions.HangDump/1.7.1) +* Microsoft.Testing.Extensions.HotReload: [1.7.1](https://www.nuget.org/packages/Microsoft.Testing.Extensions.HotReload/1.7.1) +* Microsoft.Testing.Extensions.Retry: [1.7.1](https://www.nuget.org/packages/Microsoft.Testing.Extensions.Retry/1.7.1) +* Microsoft.Testing.Extensions.TrxReport: [1.7.1](https://www.nuget.org/packages/Microsoft.Testing.Extensions.TrxReport/1.7.1) +* Microsoft.Testing.Extensions.AzureDevOpsReport [1.0.0-alpha.25277.3](https://www.nuget.org/packages/Microsoft.Testing.Extensions.AzureDevOpsReport/1.0.0-alpha.25277.3) +* MSTest.SourceGeneration: [1.0.0-alpha.25277.3](https://www.nuget.org/packages/MSTest.SourceGeneration/1.0.0-alpha.25277.3) +* MSTest.Engine: [1.0.0-alpha.25277.3](https://www.nuget.org/packages/MSTest.Engine/1.0.0-alpha.25277.3) + +## [3.9.0] - 2025-05-20 + +See full log [of v3.8.3...v3.9.0](https://github.com/microsoft/testfx/compare/v3.8.3...v3.9.0) + +### Added + +* Allow async test methods for UITestMethod on UWP and WinUI by @Youssef1313 in [#5297](https://github.com/microsoft/testfx/pull/5297) +* Add analyzer for duplicate data row by @Youssef1313 in [#5144](https://github.com/microsoft/testfx/pull/5144) +* Add `Func` overloads for Assert.Throws[Exactly] by @Youssef1313 in [#5313](https://github.com/microsoft/testfx/pull/5313) +* Add TestRunCount to TestContext by @Youssef1313 in [#5425](https://github.com/microsoft/testfx/pull/5425) + +### Fixed + +* Fix ClassCleanup not called when the first test in class is ignored by @Youssef1313 in [#5070](https://github.com/microsoft/testfx/pull/5070) +* Write warnings outside of appdomain by @nohwnd in [#5371](https://github.com/microsoft/testfx/pull/5371) +* Fix MSTEST0038 message by @Youssef1313 in [#5008](https://github.com/microsoft/testfx/pull/5008) +* Fix parameterized test treated as ignored when using VSTest in Test Explorer by @Youssef1313 in [#5020](https://github.com/microsoft/testfx/pull/5020) +* Avoid handling tuples for test methods with only object[] parameter by @Youssef1313 in [#5013](https://github.com/microsoft/testfx/pull/5013) +* Follow-up to ignore fix by @Youssef1313 in [#5042](https://github.com/microsoft/testfx/pull/5042) +* Fix discard handling for newer Assert.Throws codefix by @Youssef1313 in [#5117](https://github.com/microsoft/testfx/pull/5117) +* Ship props/targets of MSTest.TestFramework and MSTest.TestAdapter in both build and buildTransitive by @Youssef1313 in [#5220](https://github.com/microsoft/testfx/pull/5220) +* Fix TestFailedException outcome not propagating to TestResult outcome by @Youssef1313 in [#5236](https://github.com/microsoft/testfx/pull/5236) +* Fix stackoverflow in Assert.DoesNotContain by @Youssef1313 in [#5275](https://github.com/microsoft/testfx/pull/5275) +* Fix typo in docs for StringAssert.That by @YoshiRulz in [#5281](https://github.com/microsoft/testfx/pull/5281) +* Fix TypeCache re-calculating info when running in parallel by @Youssef1313 in [#5291](https://github.com/microsoft/testfx/pull/5291) +* Fix test property not considering the test class correctly by @Youssef1313 in [#5293](https://github.com/microsoft/testfx/pull/5293) +* Fix typo in TestMethodAttribute documentation by @bjornhellander in [#5300](https://github.com/microsoft/testfx/pull/5300) +* Fix parameterized UI tests for WinUI by @Youssef1313 in [#5305](https://github.com/microsoft/testfx/pull/5305) +* Fix MSTEST0032 false positive with nullability analysis by @Youssef1313 in [#5315](https://github.com/microsoft/testfx/pull/5315) +* Move System.Threading.Tasks.Extensions to TestFramework by @Youssef1313 in [#5330](https://github.com/microsoft/testfx/pull/5330) +* remove redundant null check in WriteExceptionAsync by @SimonCropp in [#5393](https://github.com/microsoft/testfx/pull/5393) +* remove redundant null checks in DeploymentItemUtility by @SimonCropp in [#5399](https://github.com/microsoft/testfx/pull/5399) +* add disable CS0618 to MSTestSettingsTests by @SimonCropp in [#5389](https://github.com/microsoft/testfx/pull/5389) +* remove redundant catch by @SimonCropp in [#5376](https://github.com/microsoft/testfx/pull/5376) +* remove redundant ReflectHelper constructor by @SimonCropp in [#5379](https://github.com/microsoft/testfx/pull/5379) +* remove redundant braces and mark as error in rider and R# by @SimonCropp in [#5380](https://github.com/microsoft/testfx/pull/5380) +* avoid redundant where and mark as an error for rider and r# by @SimonCropp in [#5384](https://github.com/microsoft/testfx/pull/5384) +* Update MSTestSettingsTests.cs by @SimonCropp in [#5388](https://github.com/microsoft/testfx/pull/5388) +* remove redundant null checks in UnitTestRunnerTests by @SimonCropp in [#5390](https://github.com/microsoft/testfx/pull/5390) +* remove redundant null checks in AssemblyEnumeratorTests by @SimonCropp in [#5391](https://github.com/microsoft/testfx/pull/5391) +* remove redundant null check in UnitTestElement by @SimonCropp in [#5400](https://github.com/microsoft/testfx/pull/5400) +* fix nullability in TestableMSTestAdapterSettings by @SimonCropp in [#5387](https://github.com/microsoft/testfx/pull/5387) +* Downgrade error entries in .editorconfig to warning by @Youssef1313 in [#5416](https://github.com/microsoft/testfx/pull/5416) +* Conditional expression can be rewritten as null-coalescing and mark as warning by @SimonCropp in [#5429](https://github.com/microsoft/testfx/pull/5429) +* remove un-used IParameterInfo by @SimonCropp in [#5431](https://github.com/microsoft/testfx/pull/5431) +* remove Trimming, AOT, and SingleFile config from MSTest.SourceGeneration by @SimonCropp in [#5433](https://github.com/microsoft/testfx/pull/5433) +* Add test for TestContext.TestRunCount by @Youssef1313 in [#5440](https://github.com/microsoft/testfx/pull/5440) +* Add test for TestProperty attribute by @Youssef1313 in [#5439](https://github.com/microsoft/testfx/pull/5439) +* remove dead code from SourceGeneration by @SimonCropp in [#5446](https://github.com/microsoft/testfx/pull/5446) +* use-char-based-replace-in-string by @SimonCropp in [#5447](https://github.com/microsoft/testfx/pull/5447) +* remove un-used static class EquatableArray by @SimonCropp in [#5445](https://github.com/microsoft/testfx/pull/5445) +* Fix exception in assembly initialize shown as aggregate exception by @Youssef1313 in [#5498](https://github.com/microsoft/testfx/pull/5498) +* Skip analyzing TestContext fields that are generated via primary constructor parameters by @Youssef1313 in [#5501](https://github.com/microsoft/testfx/pull/5501) +* Fix MSTEST0017 (actual/expected order) false negative for conversions by @Youssef1313 in [#5502](https://github.com/microsoft/testfx/pull/5502) +* Avoid overwriting TestFailureException by @Youssef1313 in [#5505](https://github.com/microsoft/testfx/pull/5505) +* Add some unit tests for Assert.Contains/DoesNotContain by @Evangelink in [#5541](https://github.com/microsoft/testfx/pull/5541) +* Fix false positive of Assert.IsTrue(x == null) when x is a pointer type by @Youssef1313 in [#5548](https://github.com/microsoft/testfx/pull/5548) + +### Artifacts + +* MSTest: [3.9.0](https://www.nuget.org/packages/MSTest/3.9.0) +* MSTest.TestFramework: [3.9.0](https://www.nuget.org/packages/MSTest.TestFramework/3.9.0) +* MSTest.TestAdapter: [3.9.0](https://www.nuget.org/packages/MSTest.TestAdapter/3.9.0) +* MSTest.Analyzers: [3.9.0](https://www.nuget.org/packages/MSTest.Analyzers/3.9.0) +* MSTest.Sdk: [3.9.0](https://www.nuget.org/packages/MSTest.Sdk/3.9.0) +* Microsoft.Testing.Extensions.CrashDump: [1.7.0](https://www.nuget.org/packages/Microsoft.Testing.Extensions.CrashDump/1.7.0) +* Microsoft.Testing.Extensions.HangDump: [1.7.0](https://www.nuget.org/packages/Microsoft.Testing.Extensions.HangDump/1.7.0) +* Microsoft.Testing.Extensions.HotReload: [1.7.0](https://www.nuget.org/packages/Microsoft.Testing.Extensions.HotReload/1.7.0) +* Microsoft.Testing.Extensions.Retry: [1.7.0](https://www.nuget.org/packages/Microsoft.Testing.Extensions.Retry/1.7.0) +* Microsoft.Testing.Extensions.TrxReport: [1.7.0](https://www.nuget.org/packages/Microsoft.Testing.Extensions.TrxReport/1.7.0) +* Microsoft.Testing.Extensions.AzureDevOpsReport [1.0.0-alpha.25256.6](https://www.nuget.org/packages/Microsoft.Testing.Extensions.AzureDevOpsReport/1.0.0-alpha.25256.6) +* MSTest.SourceGeneration: [1.0.0-alpha.25256.6](https://www.nuget.org/packages/MSTest.SourceGeneration/1.0.0-alpha.25256.6) +* MSTest.Engine: [1.0.0-alpha.25256.6](https://www.nuget.org/packages/MSTest.Engine/1.0.0-alpha.25256.6) + ## [3.8.3] - 2025-03-17 -See full log [here](https://github.com/microsoft/testfx/compare/v3.8.2...v3.8.3) +See full log [of v3.8.2...v3.8.3](https://github.com/microsoft/testfx/compare/v3.8.2...v3.8.3) ### Fixed @@ -33,7 +384,7 @@ See full log [here](https://github.com/microsoft/testfx/compare/v3.8.2...v3.8.3) ## [3.8.2] - 2025-02-19 -See full log [here](https://github.com/microsoft/testfx/compare/v3.8.1...v3.8.2) +See full log [of v3.8.1...v3.8.2](https://github.com/microsoft/testfx/compare/v3.8.1...v3.8.2) ### Fixed @@ -56,7 +407,7 @@ See full log [here](https://github.com/microsoft/testfx/compare/v3.8.1...v3.8.2) ## [3.8.1] - 2025-02-18 -See full log [here](https://github.com/microsoft/testfx/compare/v3.8.0...v3.8.1) +See full log [of v3.8.0...v3.8.1](https://github.com/microsoft/testfx/compare/v3.8.0...v3.8.1) ### Fixed @@ -83,7 +434,7 @@ See full log [here](https://github.com/microsoft/testfx/compare/v3.8.0...v3.8.1) ## [3.8.0] - 2025-02-12 -See full log [here](https://github.com/microsoft/testfx/compare/v3.7.3...v3.8.0) +See full log [of v3.7.3...v3.8.0](https://github.com/microsoft/testfx/compare/v3.7.3...v3.8.0) ### Added @@ -194,7 +545,7 @@ See full log [here](https://github.com/microsoft/testfx/compare/v3.7.3...v3.8.0) ## [3.7.3] - 2025-01-27 -See full log [here](https://github.com/microsoft/testfx/compare/v3.7.2...v3.7.3) +See full log [of v3.7.2...v3.7.3](https://github.com/microsoft/testfx/compare/v3.7.2...v3.7.3) ### Fixed @@ -215,7 +566,7 @@ See full log [here](https://github.com/microsoft/testfx/compare/v3.7.2...v3.7.3) ## [3.7.2] - 2025-01-21 -See full log [here](https://github.com/microsoft/testfx/compare/v3.7.1...v3.7.2) +See full log [of v3.7.1...v3.7.2](https://github.com/microsoft/testfx/compare/v3.7.1...v3.7.2) ### Fixed @@ -237,7 +588,7 @@ See full log [here](https://github.com/microsoft/testfx/compare/v3.7.1...v3.7.2) ## [3.7.1] - 2024-01-13 -See full log [here](https://github.com/microsoft/testfx/compare/v3.7.0...v3.7.1) +See full log [of v3.7.0...v3.7.1](https://github.com/microsoft/testfx/compare/v3.7.0...v3.7.1) ### Fixed @@ -266,7 +617,7 @@ See full log [here](https://github.com/microsoft/testfx/compare/v3.7.0...v3.7.1) ## [3.7.0] - 2024-12-20 -See full log [here](https://github.com/microsoft/testfx/compare/v3.6.4...v3.7.0) +See full log [of v3.6.4...v3.7.0](https://github.com/microsoft/testfx/compare/v3.6.4...v3.7.0) ### Added @@ -396,7 +747,7 @@ See full log [here](https://github.com/microsoft/testfx/compare/v3.6.4...v3.7.0) ## [3.6.4] - 2024-12-03 -See full log [here](https://github.com/microsoft/testfx/compare/v3.6.3...v3.6.4) +See full log [of v3.6.3...v3.6.4](https://github.com/microsoft/testfx/compare/v3.6.3...v3.6.4) ### Fixed @@ -418,7 +769,7 @@ See full log [here](https://github.com/microsoft/testfx/compare/v3.6.3...v3.6.4) ## [3.6.3] - 2024-11-12 -See full log [here](https://github.com/microsoft/testfx/compare/v3.6.2...v3.6.3) +See full log [of v3.6.2...v3.6.3](https://github.com/microsoft/testfx/compare/v3.6.2...v3.6.3) ### Fixed @@ -439,7 +790,7 @@ See full log [here](https://github.com/microsoft/testfx/compare/v3.6.2...v3.6.3) ## [3.6.2] - 2024-10-31 -See full log [here](https://github.com/microsoft/testfx/compare/v3.6.1...v3.6.2) +See full log [of v3.6.1...v3.6.2](https://github.com/microsoft/testfx/compare/v3.6.1...v3.6.2) ### Fixed @@ -465,7 +816,7 @@ See full log [here](https://github.com/microsoft/testfx/compare/v3.6.1...v3.6.2) ## [3.6.1] - 2024-10-03 -See full log [here](https://github.com/microsoft/testfx/compare/v3.6.0...v3.6.1) +See full log [of v3.6.0...v3.6.1](https://github.com/microsoft/testfx/compare/v3.6.0...v3.6.1) ### Fixed @@ -492,7 +843,7 @@ See full log [here](https://github.com/microsoft/testfx/compare/v3.6.0...v3.6.1) ## [3.6.0] - 2024-09-11 -See full log [here](https://github.com/microsoft/testfx/compare/v3.5.2...v3.6.0) +See full log [of v3.5.2...v3.6.0](https://github.com/microsoft/testfx/compare/v3.5.2...v3.6.0) ### Added @@ -576,11 +927,7 @@ See full log [here](https://github.com/microsoft/testfx/compare/v3.5.2...v3.6.0) ## [3.5.2] - 2024-08-13 -See full log [here](https://github.com/microsoft/testfx/compare/v3.5.1...v3.5.2) - -### Fixed - -* Update dependencies from devdiv/DevDiv/vs-code-coverage by @dotnet-maestro in [#3533](https://github.com/microsoft/testfx/pull/3533) +See full log [of v3.5.1...v3.5.2](https://github.com/microsoft/testfx/compare/v3.5.1...v3.5.2) ### Artifacts @@ -597,7 +944,7 @@ See full log [here](https://github.com/microsoft/testfx/compare/v3.5.1...v3.5.2) ## [3.5.1] - 2024-08-05 -See full log [here](https://github.com/microsoft/testfx/compare/v3.5.0...v3.5.1) +See full log [of v3.5.0...v3.5.1](https://github.com/microsoft/testfx/compare/v3.5.0...v3.5.1) ### Fixed @@ -620,7 +967,7 @@ See full log [here](https://github.com/microsoft/testfx/compare/v3.5.0...v3.5.1) ## [3.5.0] - 2024-07-15 -See full log [here](https://github.com/microsoft/testfx/compare/v3.4.3...v3.5.0) +See full log [of v3.4.3...v3.5.0](https://github.com/microsoft/testfx/compare/v3.4.3...v3.5.0) ### Added @@ -724,7 +1071,7 @@ See full log [here](https://github.com/microsoft/testfx/compare/v3.4.3...v3.5.0) ## [3.4.3] - 2024-05-30 -See full log [here](https://github.com/microsoft/testfx/compare/v3.4.2...v3.4.3) +See full log [of v3.4.2...v3.4.3](https://github.com/microsoft/testfx/compare/v3.4.2...v3.4.3) ### Fixed @@ -745,7 +1092,7 @@ See full log [here](https://github.com/microsoft/testfx/compare/v3.4.2...v3.4.3) ## [3.4.2] - 2024-05-30 -See full log [here](https://github.com/microsoft/testfx/compare/v3.4.1...v3.4.2) +See full log [of v3.4.1...v3.4.2](https://github.com/microsoft/testfx/compare/v3.4.1...v3.4.2) ### Fixed @@ -768,7 +1115,7 @@ See full log [here](https://github.com/microsoft/testfx/compare/v3.4.1...v3.4.2) ## [3.4.1] - 2024-05-27 -See full log [here](https://github.com/microsoft/testfx/compare/v3.4.0...v3.4.1) +See full log [of v3.4.0...v3.4.1](https://github.com/microsoft/testfx/compare/v3.4.0...v3.4.1) ### Fixed @@ -789,7 +1136,7 @@ See full log [here](https://github.com/microsoft/testfx/compare/v3.4.0...v3.4.1) ## [3.4.0] - 2024-05-23 -See full log [here](https://github.com/microsoft/testfx/compare/v3.3.1...v3.4.0) +See full log [of v3.3.1...v3.4.0](https://github.com/microsoft/testfx/compare/v3.3.1...v3.4.0) ### Added @@ -884,7 +1231,7 @@ See full log [here](https://github.com/microsoft/testfx/compare/v3.3.1...v3.4.0) ## [3.3.1] - 2024-04-04 -See full log [here](https://github.com/microsoft/testfx/compare/v3.3.0...v3.3.1) +See full log [of v3.3.0...v3.3.1](https://github.com/microsoft/testfx/compare/v3.3.0...v3.3.1) ### Fixed @@ -905,7 +1252,7 @@ See full log [here](https://github.com/microsoft/testfx/compare/v3.3.0...v3.3.1) ## [3.3.0] - 2024-04-03 -See full log [here](https://github.com/microsoft/testfx/compare/v3.2.2...v3.3.0) +See full log [of v3.2.2...v3.3.0](https://github.com/microsoft/testfx/compare/v3.2.2...v3.3.0) ### Added @@ -978,7 +1325,7 @@ See full log [here](https://github.com/microsoft/testfx/compare/v3.2.2...v3.3.0) ## [3.2.2] - 2024-02-22 -See full log [here](https://github.com/microsoft/testfx/compare/v3.2.1...v3.2.2) +See full log [of v3.2.1...v3.2.2](https://github.com/microsoft/testfx/compare/v3.2.1...v3.2.2) ### Fixed @@ -1002,16 +1349,13 @@ See full log [here](https://github.com/microsoft/testfx/compare/v3.2.1...v3.2.2) ## [3.2.1] - 2024-02-13 -See full log [here](https://github.com/microsoft/testfx/compare/v3.2.0...v.3.2.1) +See full log [of v3.2.0...v.3.2.1](https://github.com/microsoft/testfx/compare/v3.2.0...v.3.2.1) ### Fixed * MSTEST0002: fix false-positive with static TestClass (#2182) by @Evangelink in [#2199](https://github.com/microsoft/testfx/pull/2199) * Bump version of coverage and platform by @Evangelink in [#2280](https://github.com/microsoft/testfx/pull/2280) -* [rel/3.2] Update dependencies from devdiv/DevDiv/vs-code-coverage by @dotnet-maestro in [#2315](https://github.com/microsoft/testfx/pull/2315) * Fix command line output validation (#2314) by @MarcoRossignoli in [#2317](https://github.com/microsoft/testfx/pull/2317) -* [rel/3.2] Update dependencies from microsoft/testanywhere by @dotnet-maestro in [#2320](https://github.com/microsoft/testfx/pull/2320) -* [rel/3.2] Update dependencies from microsoft/testanywhere by @dotnet-maestro in [#2326](https://github.com/microsoft/testfx/pull/2326) ### Housekeeping @@ -1032,7 +1376,7 @@ See full log [here](https://github.com/microsoft/testfx/compare/v3.2.0...v.3.2.1 ## [3.2.0] - 2024-01-24 -See full log [here](https://github.com/microsoft/testfx/compare/v3.1.1...v.3.2.0) +See full log [of v3.1.1...v.3.2.0](https://github.com/microsoft/testfx/compare/v3.1.1...v.3.2.0) ### Added @@ -1113,7 +1457,7 @@ See full log [here](https://github.com/microsoft/testfx/compare/v3.1.1...v.3.2.0 ## [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) +See full log [of v3.2.0-preview.23623.1...v3.2.0-preview.24069.3](https://github.com/microsoft/testfx/compare/v3.2.0-preview.23623.1...v3.2.0-preview.24069.3) ### Added @@ -1166,7 +1510,7 @@ See full log [here](https://github.com/microsoft/testfx/compare/v3.2.0-preview.2 ## [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) +See full log [of v3.2.0-preview.23622.1...3.2.0-preview.23623.1](https://github.com/microsoft/testfx/compare/v3.2.0-preview.23622.1...3.2.0-preview.23623.1) ### Fixed @@ -1181,7 +1525,7 @@ See full log [here](https://github.com/Microsoft/testfx/compare/v3.2.0-preview.2 ## [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) +See full log [of v3.1.1...v3.2.0-preview.23622.1](https://github.com/microsoft/testfx/compare/v3.1.1...v3.2.0-preview.23622.1) ### Added @@ -1233,7 +1577,7 @@ See full log [here](https://github.com/Microsoft/testfx/compare/v3.1.1...v3.2.0- * Artifact `3.1.0` was corrupted during pipeline and for security reasons we cannot regenerate it. -See full log [here](https://github.com/Microsoft/testfx/compare/v3.1.0...v3.1.1) +See full log [of v3.1.0...v3.1.1](https://github.com/microsoft/testfx/compare/v3.1.0...v3.1.1) ### Artifacts @@ -1243,7 +1587,7 @@ See full log [here](https://github.com/Microsoft/testfx/compare/v3.1.0...v3.1.1) ## [3.1.0] - 2023-07-14 -See full log [here](https://github.com/Microsoft/testfx/compare/v3.0.4...v3.1.0) +See full log [of v3.0.4...v3.1.0](https://github.com/microsoft/testfx/compare/v3.0.4...v3.1.0) ### Added @@ -1289,7 +1633,7 @@ See full log [here](https://github.com/Microsoft/testfx/compare/v3.0.4...v3.1.0) ## [3.0.4] - 2023-06-01 -See full log [here](https://github.com/microsoft/testfx/compare/v3.0.3...v3.0.4) +See full log [of v3.0.3...v3.0.4](https://github.com/microsoft/testfx/compare/v3.0.3...v3.0.4) ### Fixed @@ -1304,7 +1648,7 @@ See full log [here](https://github.com/microsoft/testfx/compare/v3.0.3...v3.0.4) ## [3.0.3] - 2023-05-24 -See full log [here](https://github.com/Microsoft/testfx/compare/v3.0.2...v3.0.3) +See full log [of v3.0.2...v3.0.3](https://github.com/microsoft/testfx/compare/v3.0.2...v3.0.3) ### Changed @@ -1325,7 +1669,7 @@ See full log [here](https://github.com/Microsoft/testfx/compare/v3.0.2...v3.0.3) ## [3.0.2] - 2022-12-27 -See full log [here](https://github.com/microsoft/testfx/compare/v3.0.1...v3.0.2) +See full log [of v3.0.1...v3.0.2](https://github.com/microsoft/testfx/compare/v3.0.1...v3.0.2) ### Fixed @@ -1339,7 +1683,7 @@ See full log [here](https://github.com/microsoft/testfx/compare/v3.0.1...v3.0.2) ## [3.0.1] - 2022-12-20 -See full log [here](https://github.com/microsoft/testfx/compare/v3.0.0...v3.0.1) +See full log [of v3.0.0...v3.0.1](https://github.com/microsoft/testfx/compare/v3.0.0...v3.0.1) ### Fixed @@ -1362,7 +1706,7 @@ See full log [here](https://github.com/microsoft/testfx/compare/v3.0.0...v3.0.1) ## [3.0.0] - 2022-12-06 -See full log [here](https://github.com/microsoft/testfx/compare/v2.2.10...v3.0.0) +See full log [of v2.2.10...v3.0.0](https://github.com/microsoft/testfx/compare/v2.2.10...v3.0.0) Breaking changes announcements [#1274](https://github.com/microsoft/testfx/issues/1274) @@ -1419,7 +1763,7 @@ Breaking changes announcements [#1274](https://github.com/microsoft/testfx/issue ## [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) +See full log [of v3.0.0-preview-20221110-04...v3.0.0-preview-20221122-01](https://github.com/microsoft/testfx/compare/v3.0.0-preview-20221110-04...v3.0.0-preview-20221122-01) ### Added @@ -1463,7 +1807,7 @@ See full log [here](https://github.com/microsoft/testfx/compare/v3.0.0-preview-2 ## [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) +See full log [of v2.3.0-preview-20220810-02...v3.0.0-preview-20221110-04](https://github.com/microsoft/testfx/compare/v2.3.0-preview-20220810-02...v3.0.0-preview-20221110-04) ### Added @@ -1596,7 +1940,7 @@ See full log [here](https://github.com/microsoft/testfx/compare/v2.3.0-preview-2 ## [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) +A list of changes since last release are available [of v2.2.10...v2.3.0-preview-20220810-02](https://github.com/microsoft/testfx/compare/v2.2.10...v2.3.0-preview-20220810-02) ### Added @@ -1611,7 +1955,6 @@ A list of changes since last release are available [here](https://github.com/mic * [Assert failure messages](https://github.com/microsoft/testfx/pull/1172) * [Ensure assertions do not fail with FormatException](https://github.com/microsoft/testfx/pull/1126) * [Prevent format exceptions when parameters array is empty](https://github.com/microsoft/testfx/pull/1124) -* [\[main\] Update dependencies from dotnet/arcade](https://github.com/microsoft/testfx/pull/1098) ### Fixed @@ -1629,7 +1972,7 @@ A list of changes since last release are available [here](https://github.com/mic ## [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) +A list of changes since last release are available [of v2.2.10-preview-20220414-01...v2.2.10](https://github.com/microsoft/testfx/compare/v2.2.10-preview-20220414-01...v2.2.10) ### Added @@ -1641,7 +1984,6 @@ A list of changes since last release are available [here](https://github.com/mic * [Update description of the Nuget packages](https://github.com/microsoft/testfx/pull/981) * [Converted files to utf-8 so they can be diffed.](https://github.com/microsoft/testfx/pull/1070) -* [Update dependencies from https://github.com/dotnet/arcade build 20220425.6](https://github.com/microsoft/testfx/pull/1087) * [Run dotnet format whitespace](https://github.com/microsoft/testfx/pull/1085) ### Fixed @@ -1669,7 +2011,7 @@ A list of changes since last release are available [here](https://github.com/mic ## [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) +A list of changes since last release are available [of v2.2.8...v2.2.9](https://github.com/microsoft/testfx/compare/v2.2.8...v2.2.9) ### Parallel output @@ -1703,7 +2045,7 @@ Due to the way that class and assembly initialize, and cleanup are invoked, thei ## [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) +A list of changes since last release are available [of v2.2.7...v2.2.8](https://github.com/microsoft/testfx/compare/v2.2.7...v2.2.8) ### Added @@ -1735,7 +2077,7 @@ A list of changes since last release are available [here](https://github.com/mic ## [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) +A list of changes since last release are available [of v2.2.6...v2.2.7](https://github.com/microsoft/testfx/compare/v2.2.6...v2.2.7) ### Changed @@ -1752,7 +2094,7 @@ A list of changes since last release are available [here](https://github.com/mic ## [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) +A list of changes since last release are available [of v2.2.5...v2.2.6](https://github.com/microsoft/testfx/compare/v2.2.5...v2.2.6) ### Changed @@ -1770,7 +2112,7 @@ A list of changes since last release are available [here](https://github.com/mic ## [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) +A list of changes since last release are available [of v2.2.4...v2.2.5](https://github.com/microsoft/testfx/compare/v2.2.4...v2.2.5) ### Added @@ -1795,7 +2137,7 @@ A list of changes since last release are available [here](https://github.com/mic ## [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) +A list of changes since last release are available [of 0b95a26282eae17f896d732381e5c77b9a603382...v2.2.4](https://github.com/microsoft/testfx/compare/0b95a26282eae17f896d732381e5c77b9a603382...v2.2.4) ### Artifacts @@ -1804,7 +2146,7 @@ A list of changes since last release are available [here](https://github.com/mic ## [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) +A list of changes since last release are available [of v2.2.3...v2.2.4-preview-20210331-02](https://github.com/microsoft/testfx/compare/v2.2.3...v2.2.4-preview-20210331-02) ### Added @@ -1826,7 +2168,7 @@ A list of changes since last release are available [here](https://github.com/mic ## [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) +A list of changes since last release are available [of v2.2.2...v2.2.3](https://github.com/microsoft/testfx/compare/v2.2.2...v2.2.3) ### Added @@ -1839,7 +2181,7 @@ A list of changes since last release are available [here](https://github.com/mic ## [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) +A list of changes since last release are available [of v2.2.1...v2.2.2](https://github.com/microsoft/testfx/compare/v2.2.1...v2.2.2) ### Added @@ -1858,7 +2200,7 @@ A list of changes since last release are available [here](https://github.com/mic ## [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) +A list of changes since last release are available [of v2.2.0-preview-20210115-03...v2.2.1](https://github.com/microsoft/testfx/compare/v2.2.0-preview-20210115-03...v2.2.1) ### Added @@ -1887,7 +2229,7 @@ A list of changes since last release are available [here](https://github.com/mic ## [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) +A list of changes since last release are available [of v2.2.0-preview-20201126-03...v2.2.0-preview-20210115-03](https://github.com/microsoft/testfx/compare/v2.2.0-preview-20201126-03...v2.2.0-preview-20210115-03) ### Changed @@ -1914,7 +2256,7 @@ A list of changes since last release are available [here](https://github.com/mic ## [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) +A list of changes since last release are available [of v2.1.2...v2.2.0-preview-20201126-03](https://github.com/microsoft/testfx/compare/v2.1.2...v2.2.0-preview-20201126-03) ### Added @@ -1944,7 +2286,7 @@ A list of changes since last release are available [here](https://github.com/mic ## [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) +A list of changes since last release are available [of v2.1.1...v2.1.2](https://github.com/microsoft/testfx/compare/v2.1.1...v2.1.2) ### Changed @@ -1965,7 +2307,7 @@ A list of changes since last release are available [here](https://github.com/mic ## [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) +A list of changes since last release are available [of v2.1.0...v2.1.1](https://github.com/microsoft/testfx/compare/v2.1.0...v2.1.1) ### Added @@ -1992,7 +2334,7 @@ A list of changes since last release are available [here](https://github.com/mic ## [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) +A list of changes since last release are available [of v2.1.0-beta2...v2.1.0](https://github.com/microsoft/testfx/compare/v2.1.0-beta2...v2.1.0) ### Changed @@ -2010,7 +2352,7 @@ A list of changes since last release are available [here](https://github.com/mic ## [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) +A list of changes since last release are available [of v2.1.0-beta...v2.1.0-beta2](https://github.com/microsoft/testfx/compare/v2.1.0-beta...v2.1.0-beta2) ### Changed @@ -2023,7 +2365,7 @@ A list of changes since last release are available [here](https://github.com/Mic ## [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) +A list of changes since last release are available [of v2.0.0...v2.1.0-beta](https://github.com/microsoft/testfx/compare/v2.0.0...v2.1.0-beta) ### Fixed @@ -2037,7 +2379,7 @@ A list of changes since last release are available [here](https://github.com/Mic ## [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) +A list of changes since last release are available [of v2.0.0-beta4...v2.0.0](https://github.com/microsoft/testfx/compare/v2.0.0-beta4...v2.0.0) ### Added @@ -2061,7 +2403,7 @@ A list of changes since last release are available [here](https://github.com/Mic ## [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) +A list of changes since last release are available [of 2.0.0-beta2...v2.0.0-beta4](https://github.com/microsoft/testfx/compare/2.0.0-beta2...v2.0.0-beta4) ### Changed @@ -2076,7 +2418,7 @@ A list of changes since last release are available [here](https://github.com/Mic ## [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) +A list of changes since last release are available [of 1.4.0...2.0.0-beta2](https://github.com/microsoft/testfx/compare/1.4.0...2.0.0-beta2) ### Changed @@ -2091,7 +2433,7 @@ A list of changes since last release are available [here](https://github.com/Mic ## [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) +A list of changes since last release are available [of 1.4.0-beta...1.4.0](https://github.com/microsoft/testfx/compare/1.4.0-beta...1.4.0) ### Added @@ -2113,7 +2455,7 @@ A list of changes since last release are available [here](https://github.com/Mic ## [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) +A list of changes since last release are available [of 1.3.2...1.4.0-beta](https://github.com/microsoft/testfx/compare/1.3.2...1.4.0-beta) ### Added @@ -2131,7 +2473,7 @@ A list of changes since last release are available [here](https://github.com/Mic ## [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) +A list of changes since last release are available [of v1.3.1...v1.3.2](https://github.com/microsoft/testfx/compare/v1.3.1...v1.3.2) ### Changed @@ -2144,7 +2486,7 @@ A list of changes since last release are available [here](https://github.com/Mic ## [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) +A list of changes since last release are available [of v1.3.0...v1.3.1](https://github.com/microsoft/testfx/compare/v1.3.0...v1.3.1) ### Changed @@ -2158,7 +2500,7 @@ A list of changes since last release are available [here](https://github.com/Mic ## [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) +A list of changes since last release are available [of v1.2.1...v1.3.0](https://github.com/microsoft/testfx/compare/v1.2.1...v1.3.0) ### Changed @@ -2180,7 +2522,7 @@ A list of changes since last release are available [here](https://github.com/Mic ## [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) +A list of changes since last release are available [of v1.2.0...v1.3.0-beta2](https://github.com/microsoft/testfx/compare/v1.2.0...v1.3.0-beta2) ### Added @@ -2223,7 +2565,7 @@ A list of changes since last release are available [here](https://github.com/Mic ## [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) +A list of changes since last release are available [of v1.2.0-beta3...v1.2.0](https://github.com/microsoft/testfx/compare/v1.2.0-beta3...v1.2.0) ### Added @@ -2243,7 +2585,7 @@ A list of changes since last release are available [here](https://github.com/Mic ## [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) +A list of changes since last release are available [of v1.2.0-beta...v1.2.0-beta3](https://github.com/microsoft/testfx/compare/v1.2.0-beta...v1.2.0-beta3) ### Added @@ -2263,7 +2605,7 @@ A list of changes since last release are available [here](https://github.com/Mic ## [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) +A list of changes since last release are available [of v1.1.18...v1.2.0-beta](https://github.com/microsoft/testfx/compare/v1.1.18...v1.2.0-beta) ### Changed @@ -2278,7 +2620,7 @@ A list of changes since last release are available [here](https://github.com/Mic ## [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) +A list of changes since last release are available [of v1.1.17...v1.1.18](https://github.com/microsoft/testfx/compare/v1.1.17...v1.1.18) ### Changed @@ -2296,7 +2638,7 @@ A list of changes since last release are available [here](https://github.com/Mic ## [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) +A list of changes since last release are available [of v1.1.14...v1.1.17](https://github.com/microsoft/testfx/compare/v1.1.14...v1.1.17) ### Changed @@ -2314,7 +2656,7 @@ A list of changes since last release are available [here](https://github.com/Mic ## [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) +A list of changes since last release are available [of v1.1.13...v1.1.14](https://github.com/microsoft/testfx/compare/v1.1.13...v1.1.14) ### Changed diff --git a/docs/README.md b/docs/README.md index 5096491634..f476da5c8e 100644 --- a/docs/README.md +++ b/docs/README.md @@ -4,7 +4,7 @@ The following official [learn.microsoft.com website](https://learn.microsoft.com This [blog post](https://devblogs.microsoft.com/devops/mstest-v2-now-and-ahead/) announces the vision for MSTest V2. -For API documentation refer [here](https://docs.microsoft.com/dotnet/api/microsoft.visualstudio.testtools.unittesting). +For API documentation refer [to Microsoft Documentation](https://docs.microsoft.com/dotnet/api/microsoft.visualstudio.testtools.unittesting). ## Contributing diff --git a/docs/RFCs/007-DataSource-Attribute-VS-ITestDataSource.md b/docs/RFCs/007-DataSource-Attribute-VS-ITestDataSource.md index c5e08d9f65..e40cc02454 100644 --- a/docs/RFCs/007-DataSource-Attribute-VS-ITestDataSource.md +++ b/docs/RFCs/007-DataSource-Attribute-VS-ITestDataSource.md @@ -7,7 +7,7 @@ ## Summary -This details the MSTest V2 framework attribute "DataSource" for data driven tests where test data can be present in an excel file, xml file, sql database or OleDb. You can refer documentation [here](https://docs.microsoft.com/dotnet/api/microsoft.visualstudio.testtools.unittesting.datasourceattribute) for more details. +This details the MSTest V2 framework attribute "DataSource" for data driven tests where test data can be present in an excel file, xml file, sql database or OleDb. You can refer to documentation [of datasourceattribute here](https://docs.microsoft.com/dotnet/api/microsoft.visualstudio.testtools.unittesting.datasourceattribute) for more details. ## Motivation diff --git a/docs/dev-guide.md b/docs/dev-guide.md index 515bfeb946..a5fbeed98d 100644 --- a/docs/dev-guide.md +++ b/docs/dev-guide.md @@ -166,3 +166,7 @@ Note that `-test` allows to run the unit tests and `-integrationTest` allows to If you are working with Visual Studio, we recommend opening it through the `open-vs.cmd` script at the repo root. This script will set all the required environment variables required so that Visual Studio picks up the locally downloaded version of the .NET SDK. If you prefer to use your machine-wide configuration, you can open Visual Studio directly. Inside Visual Studio, all projects can be built normally. All but acceptance tests can be tested directly from Visual Studio. The acceptance tests will always use the version of the NuGet packages produced in the `artifacts/packages/shipping` folder so if you have made some changes and run these tests, it's likely that the changes will not be applied. + +## Visual Studio version requirement + +If working with Visual Studio, this repository uses the new, modern, XML-based slnx solution file format (`TestFx.slnx`). This solution file can only be opened or loaded successfully using Visual Studio 2022 17.13 or higher. Opening the TestFx.slnx directly with a different version of Visual Studio installed other than Visual Studio 2022 17.13 or higher will just open the slnx file in a raw solution XML format. diff --git a/docs/mstest-runner-protocol/001-protocol-intro.md b/docs/mstest-runner-protocol/001-protocol-intro.md index 98b662980f..071933eb2e 100644 --- a/docs/mstest-runner-protocol/001-protocol-intro.md +++ b/docs/mstest-runner-protocol/001-protocol-intro.md @@ -1,7 +1,7 @@ # 001 - MSTest Runner Protocol MSTest Runner projects builds into a self-contained executable that can be invoked to run tests. -The protocol describes the communication between the client (IDE/CLI/CI) any the MSTest Runner executable (also refered to as the server). +The protocol describes the communication between the client (IDE/CLI/CI) and the MSTest Runner executable (also refered to as the server). The communication is based on JSON-RPC and describes the RPC messages sent in order to support running of tests. @@ -394,12 +394,6 @@ interface TestNode { // Example: "time.duration-ms": 45.8143, 'time.duration-ms'?: number; - // Example: "time.start-utc": "2023-06-20T11:09:41.6882661+00:00" - 'time.start-utc'?: string; - - // Example: "time.stop-utc": "2023-06-20T11:09:41.6882661+00:00" - 'time.stop-utc'?: string; - // Note: The error consists of the stacktrace, error message and also the assertion properties. // If assertion properties are missing the exception would show in the UI as: // Message: diff --git a/dotnet.config b/dotnet.config deleted file mode 100644 index d8d2aab7e5..0000000000 --- a/dotnet.config +++ /dev/null @@ -1,2 +0,0 @@ -[dotnet.test.runner] -name= "Microsoft.Testing.Platform" diff --git a/eng/Build.props b/eng/Build.props index 57aac3a459..559d7f3749 100644 --- a/eng/Build.props +++ b/eng/Build.props @@ -6,16 +6,12 @@ - + - - - - - + diff --git a/eng/TestingPlatformRunner/TestingPlatform.Runner.targets b/eng/TestingPlatformRunner/TestingPlatform.Runner.targets deleted file mode 100644 index 6088a84863..0000000000 --- a/eng/TestingPlatformRunner/TestingPlatform.Runner.targets +++ /dev/null @@ -1,91 +0,0 @@ - - - - <_TestEnvironment>%(TestToRun.EnvironmentDisplay) - <_TestAssembly>%(TestToRun.Identity) - <_TestRuntime>%(TestToRun.TestRuntime) - <_TestTimeout>%(TestToRun.TestTimeout) - <_TestRunnerAdditionalArguments>%(TestToRun.TestRunnerAdditionalArguments) - - - - <_TestResultDirectory>$([System.IO.Path]::GetDirectoryName('%(TestToRun.ResultsTrxPath)')) - <_TestResultTrxFileName>$([System.IO.Path]::GetFileName('%(TestToRun.ResultsTrxPath)')) - - - - <_TargetFileNameNoExt>$([System.IO.Path]::GetFileNameWithoutExtension('$(_TestAssembly)')) - <_TargetDir>$([System.IO.Path]::GetDirectoryName('$(_TestAssembly)'))\ - <_CoreRuntimeConfigPath>$(_TargetDir)$(_TargetFileNameNoExt).runtimeconfig.json - <_CoreDepsPath>$(_TargetDir)$(_TargetFileNameNoExt).deps.json - - <_TestRunner Condition="'%(TestToRun.Architecture)'=='x86' And Exists('$(DotNetRoot)x86\dotnet.exe')">$(DotNetRoot)x86\dotnet.exe - <_TestRunner Condition="'$(_TestRunner)'==''">$(DotNetTool) - - <_TestRunnerArgs>exec --depsfile "$(_CoreDepsPath)" --runtimeconfig "$(_CoreRuntimeConfigPath)" $(TestRuntimeAdditionalArguments) "$(_TestAssembly)" --report-trx --report-trx-filename "$(_TestResultTrxFileName)" --results-directory "$(_TestResultDirectory)" --report-azdo $(_TestRunnerAdditionalArguments) - - - - <_TestRunner Condition="'$(_TestRunner)'==''">$(_TestAssembly) - <_TestRunnerArgs>--results-directory "$(_TestResultDirectory)" $(_TestRunnerAdditionalArguments) - - - - <_TestRunnerCommand>"$(_TestRunner)" $(_TestRunnerArgs) - - - <_TestRunnerCommand Condition="'$(TestCaptureOutput)' != 'false'">$(_TestRunnerCommand) > "%(TestToRun.ResultsStdOutPath)" 2>&1 - - - - <_OutputFiles Include="%(TestToRun.ResultsTrxPath)" /> - <_OutputFiles Include="%(TestToRun.ResultsHtmlPath)" /> - <_OutputFiles Include="%(TestToRun.ResultsStdOutPath)" /> - - - - - - - - - - - - - - - - - - <_ResultsFileToDisplay>%(TestToRun.ResultsHtmlPath) - <_ResultsFileToDisplay Condition="!Exists('$(_ResultsFileToDisplay)')">%(TestToRun.ResultsStdOutPath) - - - - - - - - - - diff --git a/eng/TestingPlatformRunner/TestingPlatformRunner.targets b/eng/TestingPlatformRunner/TestingPlatformRunner.targets deleted file mode 100644 index 1814b7ea47..0000000000 --- a/eng/TestingPlatformRunner/TestingPlatformRunner.targets +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 9372be8c7c..7ba878bf3e 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -1,21 +1,29 @@ - + https://github.com/dotnet/arcade - e46d1266547513110e67a3e4709fe8ecdfb20849 + 34019881836f80da952b9e5cb702bebf7b5362c6 - + https://github.com/dotnet/arcade - e46d1266547513110e67a3e4709fe8ecdfb20849 + 34019881836f80da952b9e5cb702bebf7b5362c6 - + https://github.com/dotnet/arcade - e46d1266547513110e67a3e4709fe8ecdfb20849 + 34019881836f80da952b9e5cb702bebf7b5362c6 - + https://dev.azure.com/devdiv/DevDiv/_git/vs-code-coverage - 22fe23c5579dfe54e9ab651eb4d3b002d9d18d73 + b1e7fa5bb1a3e468b6f9d47cf61a3b5616c44713 + + + https://github.com/microsoft/testfx + fb48f61b25dd221f0b7568863080ab5339df8def + + + https://github.com/microsoft/testfx + fb48f61b25dd221f0b7568863080ab5339df8def diff --git a/eng/Versions.props b/eng/Versions.props index 73f0048b6b..16d9c6a3e2 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -1,13 +1,16 @@ - 3.9.0 + 3.11.0 - 1.7.0 + 1.9.0 preview - 10.0.0-beta.25229.4 - 17.15.0-preview.25229.5 + 11.0.0-beta.25479.7 + 18.0.4 + + 3.11.0-preview.25479.5 + 1.9.0-preview.25479.5 diff --git a/eng/build.ps1 b/eng/build.ps1 index ea65bad4fc..ee14198255 100644 --- a/eng/build.ps1 +++ b/eng/build.ps1 @@ -29,11 +29,12 @@ Param( [switch] $nativeToolsOnMachine, [switch] $help, [switch] $vs, + [switch] $vscode, [switch] $installWindowsSdk, [Parameter(ValueFromRemainingArguments=$true)][String[]]$properties ) -if ($vs) { +if ($vs -or $vscode) { . $PSScriptRoot\common\tools.ps1 # This tells .NET Core to use the bootstrapped runtime @@ -54,8 +55,19 @@ if ($vs) { # Enables the logginc of Json RPC messages if diagnostic logging for Test Explorer is enabled in Visual Studio. $env:_TestingPlatformDiagnostics_=1; - # Launch Visual Studio with the locally defined environment variables - & "$PSScriptRoot\..\TestFx.sln" + if ($vs) { + # Launch Visual Studio with the locally defined environment variables + & "$PSScriptRoot\..\TestFx.slnx" + } else { + if (Get-Command code -ErrorAction Ignore) { + & code "$PSScriptRoot\.." + } elseif (Get-Command code-insiders -ErrorAction Ignore) { + & code-insiders "$PSScriptRoot\.." + } else { + Write-Error "VS Code not found. Please install it from https://code.visualstudio.com/" + return + } + } return } diff --git a/eng/common/SetupNugetSources.ps1 b/eng/common/SetupNugetSources.ps1 index 5db4ad71ee..9445c31432 100644 --- a/eng/common/SetupNugetSources.ps1 +++ b/eng/common/SetupNugetSources.ps1 @@ -10,8 +10,8 @@ # displayName: Setup Private Feeds Credentials # condition: eq(variables['Agent.OS'], 'Windows_NT') # inputs: -# filePath: $(Build.SourcesDirectory)/eng/common/SetupNugetSources.ps1 -# arguments: -ConfigFile $(Build.SourcesDirectory)/NuGet.config -Password $Env:Token +# filePath: $(System.DefaultWorkingDirectory)/eng/common/SetupNugetSources.ps1 +# arguments: -ConfigFile $(System.DefaultWorkingDirectory)/NuGet.config -Password $Env:Token # env: # Token: $(dn-bot-dnceng-artifact-feeds-rw) # @@ -157,7 +157,7 @@ if ($dotnet31Source -ne $null) { AddPackageSource -Sources $sources -SourceName "dotnet3.1-internal-transport" -SourceEndPoint "https://pkgs.dev.azure.com/dnceng/_packaging/dotnet3.1-internal-transport/nuget/v2" -Creds $creds -Username $userName -pwd $Password } -$dotnetVersions = @('5','6','7','8','9') +$dotnetVersions = @('5','6','7','8','9','10') foreach ($dotnetVersion in $dotnetVersions) { $feedPrefix = "dotnet" + $dotnetVersion; diff --git a/eng/common/SetupNugetSources.sh b/eng/common/SetupNugetSources.sh index 4604b61b03..ddf4efc81a 100644 --- a/eng/common/SetupNugetSources.sh +++ b/eng/common/SetupNugetSources.sh @@ -11,8 +11,8 @@ # - task: Bash@3 # displayName: Setup Internal Feeds # inputs: -# filePath: $(Build.SourcesDirectory)/eng/common/SetupNugetSources.sh -# arguments: $(Build.SourcesDirectory)/NuGet.config +# filePath: $(System.DefaultWorkingDirectory)/eng/common/SetupNugetSources.sh +# arguments: $(System.DefaultWorkingDirectory)/NuGet.config # condition: ne(variables['Agent.OS'], 'Windows_NT') # - task: NuGetAuthenticate@1 # @@ -99,7 +99,7 @@ if [ "$?" == "0" ]; then PackageSources+=('dotnet3.1-internal-transport') fi -DotNetVersions=('5' '6' '7' '8' '9') +DotNetVersions=('5' '6' '7' '8' '9' '10') for DotNetVersion in ${DotNetVersions[@]} ; do FeedPrefix="dotnet${DotNetVersion}"; diff --git a/eng/common/build.ps1 b/eng/common/build.ps1 index 6b3be1916f..8cfee107e7 100644 --- a/eng/common/build.ps1 +++ b/eng/common/build.ps1 @@ -21,6 +21,7 @@ Param( [switch] $publish, [switch] $clean, [switch][Alias('pb')]$productBuild, + [switch]$fromVMR, [switch][Alias('bl')]$binaryLog, [switch][Alias('nobl')]$excludeCIBinarylog, [switch] $ci, @@ -74,6 +75,7 @@ function Print-Usage() { Write-Host " -nativeToolsOnMachine Sets the native tools on machine environment variable (indicating that the script should use native tools on machine)" Write-Host " -nodeReuse Sets nodereuse msbuild parameter ('true' or 'false')" Write-Host " -buildCheck Sets /check msbuild parameter" + Write-Host " -fromVMR Set when building from within the VMR" Write-Host "" Write-Host "Command line arguments not listed above are passed thru to msbuild." @@ -127,7 +129,8 @@ function Build { /p:Deploy=$deploy ` /p:Test=$test ` /p:Pack=$pack ` - /p:DotNetBuildRepo=$productBuild ` + /p:DotNetBuild=$productBuild ` + /p:DotNetBuildFromVMR=$fromVMR ` /p:IntegrationTest=$integrationTest ` /p:PerformanceTest=$performanceTest ` /p:Sign=$sign ` diff --git a/eng/common/build.sh b/eng/common/build.sh index 36fba82a37..ec3e80d189 100755 --- a/eng/common/build.sh +++ b/eng/common/build.sh @@ -43,6 +43,7 @@ usage() echo " --nodeReuse Sets nodereuse msbuild parameter ('true' or 'false')" echo " --warnAsError Sets warnaserror msbuild parameter ('true' or 'false')" echo " --buildCheck Sets /check msbuild parameter" + echo " --fromVMR Set when building from within the VMR" echo "" echo "Command line arguments not listed above are passed thru to msbuild." echo "Arguments can also be passed in with a single hyphen." @@ -64,6 +65,7 @@ restore=false build=false source_build=false product_build=false +from_vmr=false rebuild=false test=false integration_test=false @@ -89,8 +91,8 @@ verbosity='minimal' runtime_source_feed='' runtime_source_feed_key='' -properties='' -while [[ $# > 0 ]]; do +properties=() +while [[ $# -gt 0 ]]; do opt="$(echo "${1/#--/-}" | tr "[:upper:]" "[:lower:]")" case "$opt" in -help|-h) @@ -129,19 +131,22 @@ while [[ $# > 0 ]]; do -pack) pack=true ;; - -sourcebuild|-sb) + -sourcebuild|-source-build|-sb) build=true source_build=true product_build=true restore=true pack=true ;; - -productBuild|-pb) + -productbuild|-product-build|-pb) build=true product_build=true restore=true pack=true ;; + -fromvmr|-from-vmr) + from_vmr=true + ;; -test|-t) test=true ;; @@ -187,7 +192,7 @@ while [[ $# > 0 ]]; do shift ;; *) - properties="$properties $1" + properties+=("$1") ;; esac @@ -221,7 +226,7 @@ function Build { InitializeCustomToolset if [[ ! -z "$projects" ]]; then - properties="$properties /p:Projects=$projects" + properties+=("/p:Projects=$projects") fi local bl="" @@ -241,8 +246,9 @@ function Build { /p:RepoRoot="$repo_root" \ /p:Restore=$restore \ /p:Build=$build \ - /p:DotNetBuildRepo=$product_build \ + /p:DotNetBuild=$product_build \ /p:DotNetBuildSourceOnly=$source_build \ + /p:DotNetBuildFromVMR=$from_vmr \ /p:Rebuild=$rebuild \ /p:Test=$test \ /p:Pack=$pack \ @@ -251,7 +257,7 @@ function Build { /p:Sign=$sign \ /p:Publish=$publish \ /p:RestoreStaticGraphEnableBinaryLogger=$binary_log \ - $properties + ${properties[@]+"${properties[@]}"} ExitWithExitCode 0 } diff --git a/eng/common/core-templates/job/job.yml b/eng/common/core-templates/job/job.yml index 6badecba7b..5ce5184061 100644 --- a/eng/common/core-templates/job/job.yml +++ b/eng/common/core-templates/job/job.yml @@ -20,6 +20,7 @@ parameters: artifacts: '' enableMicrobuild: false enableMicrobuildForMacAndLinux: false + microbuildUseESRP: true enablePublishBuildArtifacts: false enablePublishBuildAssets: false enablePublishTestResults: false @@ -128,6 +129,7 @@ jobs: parameters: enableMicrobuild: ${{ parameters.enableMicrobuild }} enableMicrobuildForMacAndLinux: ${{ parameters.enableMicrobuildForMacAndLinux }} + microbuildUseESRP: ${{ parameters.microbuildUseESRP }} continueOnError: ${{ parameters.continueOnError }} - ${{ if and(eq(parameters.runAsPublic, 'false'), eq(variables['System.TeamProject'], 'internal')) }}: @@ -161,7 +163,7 @@ jobs: inputs: testResultsFormat: 'xUnit' testResultsFiles: '*.xml' - searchFolder: '$(Build.SourcesDirectory)/artifacts/TestResults/$(_BuildConfig)' + searchFolder: '$(System.DefaultWorkingDirectory)/artifacts/TestResults/$(_BuildConfig)' testRunTitle: ${{ coalesce(parameters.testRunTitle, parameters.name, '$(System.JobName)') }}-xunit mergeTestResults: ${{ parameters.mergeTestResults }} continueOnError: true @@ -172,7 +174,7 @@ jobs: inputs: testResultsFormat: 'VSTest' testResultsFiles: '*.trx' - searchFolder: '$(Build.SourcesDirectory)/artifacts/TestResults/$(_BuildConfig)' + searchFolder: '$(System.DefaultWorkingDirectory)/artifacts/TestResults/$(_BuildConfig)' testRunTitle: ${{ coalesce(parameters.testRunTitle, parameters.name, '$(System.JobName)') }}-trx mergeTestResults: ${{ parameters.mergeTestResults }} continueOnError: true @@ -216,7 +218,7 @@ jobs: - task: CopyFiles@2 displayName: Gather buildconfiguration for build retry inputs: - SourceFolder: '$(Build.SourcesDirectory)/eng/common/BuildConfiguration' + SourceFolder: '$(System.DefaultWorkingDirectory)/eng/common/BuildConfiguration' Contents: '**' TargetFolder: '$(Build.ArtifactStagingDirectory)/eng/common/BuildConfiguration' continueOnError: true diff --git a/eng/common/core-templates/job/onelocbuild.yml b/eng/common/core-templates/job/onelocbuild.yml index 00feec8ebb..c5788829a8 100644 --- a/eng/common/core-templates/job/onelocbuild.yml +++ b/eng/common/core-templates/job/onelocbuild.yml @@ -4,11 +4,11 @@ parameters: # Optional: A defined YAML pool - https://docs.microsoft.com/en-us/azure/devops/pipelines/yaml-schema?view=vsts&tabs=schema#pool pool: '' - + CeapexPat: $(dn-bot-ceapex-package-r) # PAT for the loc AzDO instance https://dev.azure.com/ceapex GithubPat: $(BotAccount-dotnet-bot-repo-PAT) - SourcesDirectory: $(Build.SourcesDirectory) + SourcesDirectory: $(System.DefaultWorkingDirectory) CreatePr: true AutoCompletePr: false ReusePr: true @@ -27,7 +27,7 @@ parameters: is1ESPipeline: '' jobs: - job: OneLocBuild${{ parameters.JobNameSuffix }} - + dependsOn: ${{ parameters.dependsOn }} displayName: OneLocBuild${{ parameters.JobNameSuffix }} @@ -68,7 +68,7 @@ jobs: - ${{ if ne(parameters.SkipLocProjectJsonGeneration, 'true') }}: - task: Powershell@2 inputs: - filePath: $(Build.SourcesDirectory)/eng/common/generate-locproject.ps1 + filePath: $(System.DefaultWorkingDirectory)/eng/common/generate-locproject.ps1 arguments: $(_GenerateLocProjectArguments) displayName: Generate LocProject.json condition: ${{ parameters.condition }} @@ -86,8 +86,7 @@ jobs: isAutoCompletePrSelected: ${{ parameters.AutoCompletePr }} ${{ if eq(parameters.CreatePr, true) }}: isUseLfLineEndingsSelected: ${{ parameters.UseLfLineEndings }} - ${{ if eq(parameters.RepoType, 'gitHub') }}: - isShouldReusePrSelected: ${{ parameters.ReusePr }} + isShouldReusePrSelected: ${{ parameters.ReusePr }} packageSourceAuth: patAuth patVariable: ${{ parameters.CeapexPat }} ${{ if eq(parameters.RepoType, 'gitHub') }}: @@ -100,22 +99,20 @@ jobs: mirrorBranch: ${{ parameters.MirrorBranch }} condition: ${{ parameters.condition }} - - template: /eng/common/core-templates/steps/publish-build-artifacts.yml - parameters: - is1ESPipeline: ${{ parameters.is1ESPipeline }} - args: - displayName: Publish Localization Files - pathToPublish: '$(Build.ArtifactStagingDirectory)/loc' - publishLocation: Container - artifactName: Loc - condition: ${{ parameters.condition }} + # Copy the locProject.json to the root of the Loc directory, then publish a pipeline artifact + - task: CopyFiles@2 + displayName: Copy LocProject.json + inputs: + SourceFolder: '$(System.DefaultWorkingDirectory)/eng/Localize/' + Contents: 'LocProject.json' + TargetFolder: '$(Build.ArtifactStagingDirectory)/loc' + condition: ${{ parameters.condition }} - - template: /eng/common/core-templates/steps/publish-build-artifacts.yml + - template: /eng/common/core-templates/steps/publish-pipeline-artifacts.yml parameters: is1ESPipeline: ${{ parameters.is1ESPipeline }} args: - displayName: Publish LocProject.json - pathToPublish: '$(Build.SourcesDirectory)/eng/Localize/' - publishLocation: Container - artifactName: Loc - condition: ${{ parameters.condition }} \ No newline at end of file + targetPath: '$(Build.ArtifactStagingDirectory)/loc' + artifactName: 'Loc' + displayName: 'Publish Localization Files' + condition: ${{ parameters.condition }} diff --git a/eng/common/core-templates/job/publish-build-assets.yml b/eng/common/core-templates/job/publish-build-assets.yml index 4f1dc42e02..37dff559fc 100644 --- a/eng/common/core-templates/job/publish-build-assets.yml +++ b/eng/common/core-templates/job/publish-build-assets.yml @@ -32,6 +32,16 @@ parameters: # Optional: 🌤️ or not the build has assets it wants to publish to BAR isAssetlessBuild: false + # Optional, publishing version + publishingVersion: 3 + + # Optional: A minimatch pattern for the asset manifests to publish to BAR + assetManifestsPattern: '*/manifests/**/*.xml' + + repositoryAlias: self + + officialBuildId: '' + jobs: - job: Asset_Registry_Publish @@ -54,6 +64,11 @@ jobs: value: false # unconditional - needed for logs publishing (redactor tool version) - template: /eng/common/core-templates/post-build/common-variables.yml + - name: OfficialBuildId + ${{ if ne(parameters.officialBuildId, '') }}: + value: ${{ parameters.officialBuildId }} + ${{ else }}: + value: $(Build.BuildNumber) pool: # We don't use the collection uri here because it might vary (.visualstudio.com vs. dev.azure.com) @@ -72,18 +87,36 @@ jobs: - 'Illegal entry point, is1ESPipeline is not defined. Repository yaml should not directly reference templates in core-templates folder.': error - ${{ if and(eq(parameters.runAsPublic, 'false'), ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest')) }}: - - checkout: self + - checkout: ${{ parameters.repositoryAlias }} fetchDepth: 3 clean: true - ${{ if eq(parameters.isAssetlessBuild, 'false') }}: - - task: DownloadPipelineArtifact@2 - displayName: Download Asset Manifests - inputs: - artifactName: AssetManifests - targetPath: '$(Build.StagingDirectory)/AssetManifests' - condition: ${{ parameters.condition }} - continueOnError: ${{ parameters.continueOnError }} + - ${{ if eq(parameters.publishingVersion, 3) }}: + - task: DownloadPipelineArtifact@2 + displayName: Download Asset Manifests + inputs: + artifactName: AssetManifests + targetPath: '$(Build.StagingDirectory)/AssetManifests' + condition: ${{ parameters.condition }} + continueOnError: ${{ parameters.continueOnError }} + - ${{ if eq(parameters.publishingVersion, 4) }}: + - task: DownloadPipelineArtifact@2 + displayName: Download V4 asset manifests + inputs: + itemPattern: '*/manifests/**/*.xml' + targetPath: '$(Build.StagingDirectory)/AllAssetManifests' + condition: ${{ parameters.condition }} + continueOnError: ${{ parameters.continueOnError }} + - task: CopyFiles@2 + displayName: Copy V4 asset manifests to AssetManifests + inputs: + SourceFolder: '$(Build.StagingDirectory)/AllAssetManifests' + Contents: ${{ parameters.assetManifestsPattern }} + TargetFolder: '$(Build.StagingDirectory)/AssetManifests' + flattenFolders: true + condition: ${{ parameters.condition }} + continueOnError: ${{ parameters.continueOnError }} - task: NuGetAuthenticate@1 @@ -93,12 +126,12 @@ jobs: azureSubscription: "Darc: Maestro Production" scriptType: ps scriptLocation: scriptPath - scriptPath: $(Build.SourcesDirectory)/eng/common/sdk-task.ps1 + scriptPath: $(System.DefaultWorkingDirectory)/eng/common/sdk-task.ps1 arguments: -task PublishBuildAssets -restore -msbuildEngine dotnet /p:ManifestsPath='$(Build.StagingDirectory)/AssetManifests' /p:IsAssetlessBuild=${{ parameters.isAssetlessBuild }} /p:MaestroApiEndpoint=https://maestro.dot.net - /p:OfficialBuildId=$(Build.BuildNumber) + /p:OfficialBuildId=$(OfficialBuildId) condition: ${{ parameters.condition }} continueOnError: ${{ parameters.continueOnError }} @@ -113,13 +146,24 @@ jobs: Add-Content -Path $filePath -Value "$(DefaultChannels)" Add-Content -Path $filePath -Value $(IsStableBuild) - $symbolExclusionfile = "$(Build.SourcesDirectory)/eng/SymbolPublishingExclusionsFile.txt" + $symbolExclusionfile = "$(System.DefaultWorkingDirectory)/eng/SymbolPublishingExclusionsFile.txt" if (Test-Path -Path $symbolExclusionfile) { Write-Host "SymbolExclusionFile exists" Copy-Item -Path $symbolExclusionfile -Destination "$(Build.StagingDirectory)/ReleaseConfigs" } + - ${{ if eq(parameters.publishingVersion, 4) }}: + - template: /eng/common/core-templates/steps/publish-pipeline-artifacts.yml + parameters: + is1ESPipeline: ${{ parameters.is1ESPipeline }} + args: + targetPath: '$(Build.ArtifactStagingDirectory)/MergedManifest.xml' + artifactName: AssetManifests + displayName: 'Publish Merged Manifest' + retryCountOnTaskFailure: 10 # for any logs being locked + sbomEnabled: false # we don't need SBOM for logs + - template: /eng/common/core-templates/steps/publish-build-artifacts.yml parameters: is1ESPipeline: ${{ parameters.is1ESPipeline }} @@ -142,7 +186,7 @@ jobs: azureSubscription: "Darc: Maestro Production" scriptType: ps scriptLocation: scriptPath - scriptPath: $(Build.SourcesDirectory)/eng/common/post-build/publish-using-darc.ps1 + scriptPath: $(System.DefaultWorkingDirectory)/eng/common/post-build/publish-using-darc.ps1 arguments: > -BuildId $(BARBuildId) -PublishingInfraVersion 3 diff --git a/eng/common/core-templates/jobs/codeql-build.yml b/eng/common/core-templates/jobs/codeql-build.yml index 693b00b370..dbc14ac580 100644 --- a/eng/common/core-templates/jobs/codeql-build.yml +++ b/eng/common/core-templates/jobs/codeql-build.yml @@ -24,7 +24,7 @@ jobs: - name: DefaultGuardianVersion value: 0.109.0 - name: GuardianPackagesConfigFile - value: $(Build.SourcesDirectory)\eng\common\sdl\packages.config + value: $(System.DefaultWorkingDirectory)\eng\common\sdl\packages.config - name: GuardianVersion value: ${{ coalesce(parameters.overrideGuardianVersion, '$(DefaultGuardianVersion)') }} diff --git a/eng/common/core-templates/jobs/jobs.yml b/eng/common/core-templates/jobs/jobs.yml index bf35b78faa..01ada74766 100644 --- a/eng/common/core-templates/jobs/jobs.yml +++ b/eng/common/core-templates/jobs/jobs.yml @@ -43,6 +43,8 @@ parameters: artifacts: {} is1ESPipeline: '' + repositoryAlias: self + officialBuildId: '' # Internal resources (telemetry, microbuild) can only be accessed from non-public projects, # and some (Microbuild) should only be applied to non-PR cases for internal builds. @@ -83,7 +85,6 @@ jobs: - template: /eng/common/core-templates/jobs/source-build.yml parameters: is1ESPipeline: ${{ parameters.is1ESPipeline }} - allCompletedJobId: Source_Build_Complete ${{ each parameter in parameters.sourceBuildParameters }}: ${{ parameter.key }}: ${{ parameter.value }} @@ -108,8 +109,6 @@ jobs: - ${{ if eq(parameters.publishBuildAssetsDependsOn, '') }}: - ${{ each job in parameters.jobs }}: - ${{ job.job }} - - ${{ if eq(parameters.enableSourceBuild, true) }}: - - Source_Build_Complete runAsPublic: ${{ parameters.runAsPublic }} publishAssetsImmediately: ${{ or(parameters.publishAssetsImmediately, parameters.isAssetlessBuild) }} @@ -117,3 +116,5 @@ jobs: enablePublishBuildArtifacts: ${{ parameters.enablePublishBuildArtifacts }} artifactsPublishingAdditionalParameters: ${{ parameters.artifactsPublishingAdditionalParameters }} signingValidationAdditionalParameters: ${{ parameters.signingValidationAdditionalParameters }} + repositoryAlias: ${{ parameters.repositoryAlias }} + officialBuildId: ${{ parameters.officialBuildId }} diff --git a/eng/common/core-templates/jobs/source-build.yml b/eng/common/core-templates/jobs/source-build.yml index a10ccfbee6..d92860cba2 100644 --- a/eng/common/core-templates/jobs/source-build.yml +++ b/eng/common/core-templates/jobs/source-build.yml @@ -2,19 +2,13 @@ parameters: # This template adds arcade-powered source-build to CI. A job is created for each platform, as # well as an optional server job that completes when all platform jobs complete. - # The name of the "join" job for all source-build platforms. If set to empty string, the job is - # not included. Existing repo pipelines can use this job depend on all source-build jobs - # completing without maintaining a separate list of every single job ID: just depend on this one - # server job. By default, not included. Recommended name if used: 'Source_Build_Complete'. - allCompletedJobId: '' - # See /eng/common/core-templates/job/source-build.yml jobNamePrefix: 'Source_Build' # This is the default platform provided by Arcade, intended for use by a managed-only repo. defaultManagedPlatform: name: 'Managed' - container: 'mcr.microsoft.com/dotnet-buildtools/prereqs:centos-stream9' + container: 'mcr.microsoft.com/dotnet-buildtools/prereqs:centos-stream-10-amd64' # Defines the platforms on which to run build jobs. One job is created for each platform, and the # object in this array is sent to the job template as 'platform'. If no platforms are specified, @@ -31,16 +25,6 @@ parameters: jobs: -- ${{ if ne(parameters.allCompletedJobId, '') }}: - - job: ${{ parameters.allCompletedJobId }} - displayName: Source-Build Complete - pool: server - dependsOn: - - ${{ each platform in parameters.platforms }}: - - ${{ parameters.jobNamePrefix }}_${{ platform.name }} - - ${{ if eq(length(parameters.platforms), 0) }}: - - ${{ parameters.jobNamePrefix }}_${{ parameters.defaultManagedPlatform.name }} - - ${{ each platform in parameters.platforms }}: - template: /eng/common/core-templates/job/source-build.yml parameters: diff --git a/eng/common/core-templates/post-build/post-build.yml b/eng/common/core-templates/post-build/post-build.yml index 5757915edb..f6f87fe5c6 100644 --- a/eng/common/core-templates/post-build/post-build.yml +++ b/eng/common/core-templates/post-build/post-build.yml @@ -154,7 +154,7 @@ stages: - task: PowerShell@2 displayName: Validate inputs: - filePath: $(Build.SourcesDirectory)/eng/common/post-build/nuget-validation.ps1 + filePath: $(System.DefaultWorkingDirectory)/eng/common/post-build/nuget-validation.ps1 arguments: -PackagesPath $(Build.ArtifactStagingDirectory)/PackageArtifacts/ - job: @@ -193,9 +193,6 @@ stages: buildId: $(AzDOBuildId) artifactName: PackageArtifacts checkDownloadedFiles: true - itemPattern: | - ** - !**/Microsoft.SourceBuild.Intermediate.*.nupkg # This is necessary whenever we want to publish/restore to an AzDO private feed # Since sdk-task.ps1 tries to restore packages we need to do this authentication here @@ -211,7 +208,7 @@ stages: filePath: eng\common\sdk-task.ps1 arguments: -task SigningValidation -restore -msbuildEngine vs /p:PackageBasePath='$(Build.ArtifactStagingDirectory)/PackageArtifacts' - /p:SignCheckExclusionsFile='$(Build.SourcesDirectory)/eng/SignCheckExclusionsFile.txt' + /p:SignCheckExclusionsFile='$(System.DefaultWorkingDirectory)/eng/SignCheckExclusionsFile.txt' ${{ parameters.signingValidationAdditionalParameters }} - template: /eng/common/core-templates/steps/publish-logs.yml @@ -261,7 +258,7 @@ stages: - task: PowerShell@2 displayName: Validate inputs: - filePath: $(Build.SourcesDirectory)/eng/common/post-build/sourcelink-validation.ps1 + filePath: $(System.DefaultWorkingDirectory)/eng/common/post-build/sourcelink-validation.ps1 arguments: -InputPath $(Build.ArtifactStagingDirectory)/BlobArtifacts/ -ExtractPath $(Agent.BuildDirectory)/Extract/ -GHRepoName $(Build.Repository.Name) @@ -316,7 +313,7 @@ stages: azureSubscription: "Darc: Maestro Production" scriptType: ps scriptLocation: scriptPath - scriptPath: $(Build.SourcesDirectory)/eng/common/post-build/publish-using-darc.ps1 + scriptPath: $(System.DefaultWorkingDirectory)/eng/common/post-build/publish-using-darc.ps1 arguments: > -BuildId $(BARBuildId) -PublishingInfraVersion ${{ parameters.publishingInfraVersion }} diff --git a/eng/common/core-templates/post-build/setup-maestro-vars.yml b/eng/common/core-templates/post-build/setup-maestro-vars.yml index f7602980db..a7abd58c4b 100644 --- a/eng/common/core-templates/post-build/setup-maestro-vars.yml +++ b/eng/common/core-templates/post-build/setup-maestro-vars.yml @@ -36,7 +36,7 @@ steps: $AzureDevOpsBuildId = $Env:Build_BuildId } else { - . $(Build.SourcesDirectory)\eng\common\tools.ps1 + . $(System.DefaultWorkingDirectory)\eng\common\tools.ps1 $darc = Get-Darc $buildInfo = & $darc get-build ` --id ${{ parameters.BARBuildId }} ` diff --git a/eng/common/core-templates/steps/enable-internal-sources.yml b/eng/common/core-templates/steps/enable-internal-sources.yml index 64f881bffc..4085512b69 100644 --- a/eng/common/core-templates/steps/enable-internal-sources.yml +++ b/eng/common/core-templates/steps/enable-internal-sources.yml @@ -17,8 +17,8 @@ steps: - task: PowerShell@2 displayName: Setup Internal Feeds inputs: - filePath: $(Build.SourcesDirectory)/eng/common/SetupNugetSources.ps1 - arguments: -ConfigFile $(Build.SourcesDirectory)/NuGet.config -Password $Env:Token + filePath: $(System.DefaultWorkingDirectory)/eng/common/SetupNugetSources.ps1 + arguments: -ConfigFile $(System.DefaultWorkingDirectory)/NuGet.config -Password $Env:Token env: Token: ${{ parameters.legacyCredential }} # If running on dnceng (internal project), just use the default behavior for NuGetAuthenticate. @@ -29,8 +29,8 @@ steps: - task: PowerShell@2 displayName: Setup Internal Feeds inputs: - filePath: $(Build.SourcesDirectory)/eng/common/SetupNugetSources.ps1 - arguments: -ConfigFile $(Build.SourcesDirectory)/NuGet.config + filePath: $(System.DefaultWorkingDirectory)/eng/common/SetupNugetSources.ps1 + arguments: -ConfigFile $(System.DefaultWorkingDirectory)/NuGet.config - ${{ else }}: - template: /eng/common/templates/steps/get-federated-access-token.yml parameters: @@ -39,8 +39,8 @@ steps: - task: PowerShell@2 displayName: Setup Internal Feeds inputs: - filePath: $(Build.SourcesDirectory)/eng/common/SetupNugetSources.ps1 - arguments: -ConfigFile $(Build.SourcesDirectory)/NuGet.config -Password $(dnceng-artifacts-feeds-read-access-token) + filePath: $(System.DefaultWorkingDirectory)/eng/common/SetupNugetSources.ps1 + arguments: -ConfigFile $(System.DefaultWorkingDirectory)/NuGet.config -Password $(dnceng-artifacts-feeds-read-access-token) # This is required in certain scenarios to install the ADO credential provider. # It installed by default in some msbuild invocations (e.g. VS msbuild), but needs to be installed for others # (e.g. dotnet msbuild). diff --git a/eng/common/core-templates/steps/generate-sbom.yml b/eng/common/core-templates/steps/generate-sbom.yml index 44a9636cdf..003f7eae0f 100644 --- a/eng/common/core-templates/steps/generate-sbom.yml +++ b/eng/common/core-templates/steps/generate-sbom.yml @@ -5,8 +5,8 @@ # IgnoreDirectories - Directories to ignore for SBOM generation. This will be passed through to the CG component detector. parameters: - PackageVersion: 10.0.0 - BuildDropPath: '$(Build.SourcesDirectory)/artifacts' + PackageVersion: 11.0.0 + BuildDropPath: '$(System.DefaultWorkingDirectory)/artifacts' PackageName: '.NET' ManifestDirPath: $(Build.ArtifactStagingDirectory)/sbom IgnoreDirectories: '' diff --git a/eng/common/core-templates/steps/install-microbuild.yml b/eng/common/core-templates/steps/install-microbuild.yml index 2bcf974ee1..d6b9878f54 100644 --- a/eng/common/core-templates/steps/install-microbuild.yml +++ b/eng/common/core-templates/steps/install-microbuild.yml @@ -4,8 +4,17 @@ parameters: # Enable install tasks for MicroBuild on Mac and Linux # Will be ignored if 'enableMicrobuild' is false or 'Agent.Os' is 'Windows_NT' enableMicrobuildForMacAndLinux: false + # Determines whether the ESRP service connection information should be passed to the signing plugin. + # This overlaps with _SignType to some degree. We only need the service connection for real signing. + # It's important that the service connection not be passed to the MicroBuildSigningPlugin task in this place. + # Doing so will cause the service connection to be authorized for the pipeline, which isn't allowed and won't work for non-prod. + # Unfortunately, _SignType can't be used to exclude the use of the service connection in non-real sign scenarios. The + # variable is not available in template expression. _SignType has a very large proliferation across .NET, so replacing it is tough. + microbuildUseESRP: true # Location of the MicroBuild output folder + # NOTE: There's something that relies on this being in the "default" source directory for tasks such as Signing to work properly. microBuildOutputFolder: '$(Build.SourcesDirectory)' + continueOnError: false steps: @@ -21,65 +30,62 @@ steps: workingDirectory: ${{ parameters.microBuildOutputFolder }} condition: and(succeeded(), ne(variables['Agent.Os'], 'Windows_NT')) + - script: | + REM Check if ESRP is disabled while SignType is real + if /I "${{ parameters.microbuildUseESRP }}"=="false" if /I "$(_SignType)"=="real" ( + echo Error: ESRP must be enabled when SignType is real. + exit /b 1 + ) + displayName: 'Validate ESRP usage (Windows)' + condition: and(succeeded(), eq(variables['Agent.Os'], 'Windows_NT')) + - script: | + # Check if ESRP is disabled while SignType is real + if [ "${{ parameters.microbuildUseESRP }}" = "false" ] && [ "$(_SignType)" = "real" ]; then + echo "Error: ESRP must be enabled when SignType is real." + exit 1 + fi + displayName: 'Validate ESRP usage (Non-Windows)' + condition: and(succeeded(), ne(variables['Agent.Os'], 'Windows_NT')) + + # Two different MB install steps. This is due to not being able to use the agent OS during + # YAML expansion, and Windows vs. Linux/Mac uses different service connections. However, + # we can avoid including the MB install step if not enabled at all. This avoids a bunch of + # extra pipeline authorizations, since most pipelines do not sign on non-Windows. - task: MicroBuildSigningPlugin@4 - displayName: Install MicroBuild plugin + displayName: Install MicroBuild plugin (Windows) inputs: signType: $(_SignType) zipSources: false feedSource: https://dnceng.pkgs.visualstudio.com/_packaging/MicroBuildToolset/nuget/v3/index.json - ${{ if and(eq(parameters.enableMicrobuildForMacAndLinux, 'true'), ne(variables['Agent.Os'], 'Windows_NT')) }}: - azureSubscription: 'MicroBuild Signing Task (DevDiv)' - useEsrpCli: true + ${{ if eq(parameters.microbuildUseESRP, true) }}: + ConnectedServiceName: 'MicroBuild Signing Task (DevDiv)' + ${{ if eq(variables['System.TeamProject'], 'DevDiv') }}: + ConnectedPMEServiceName: 6cc74545-d7b9-4050-9dfa-ebefcc8961ea + ${{ else }}: + ConnectedPMEServiceName: 248d384a-b39b-46e3-8ad5-c2c210d5e7ca env: TeamName: $(_TeamName) MicroBuildOutputFolderOverride: ${{ parameters.microBuildOutputFolder }} SYSTEM_ACCESSTOKEN: $(System.AccessToken) continueOnError: ${{ parameters.continueOnError }} - condition: and( - succeeded(), - or( - and( - eq(variables['Agent.Os'], 'Windows_NT'), - in(variables['_SignType'], 'real', 'test') - ), - and( - ${{ eq(parameters.enableMicrobuildForMacAndLinux, true) }}, - ne(variables['Agent.Os'], 'Windows_NT'), - eq(variables['_SignType'], 'real') - ) - )) - - # Workaround for ESRP CLI on Linux - https://github.com/dotnet/source-build/issues/4964 - - ${{ if eq(parameters.enableMicrobuildForMacAndLinux, 'true') }}: - - task: UseDotNet@2 - displayName: Install .NET 9.0 SDK for ESRP CLI Workaround - inputs: - packageType: sdk - version: 9.0.x - installationPath: ${{ parameters.microBuildOutputFolder }}/.dotnet - workingDirectory: ${{ parameters.microBuildOutputFolder }} - condition: and(succeeded(), eq(variables['Agent.OS'], 'Linux')) + condition: and(succeeded(), eq(variables['Agent.Os'], 'Windows_NT'), in(variables['_SignType'], 'real', 'test')) - - task: PowerShell@2 - displayName: Workaround for ESRP CLI on Linux + - ${{ if eq(parameters.enableMicrobuildForMacAndLinux, true) }}: + - task: MicroBuildSigningPlugin@4 + displayName: Install MicroBuild plugin (non-Windows) inputs: - targetType: 'inline' - script: | - Write-Host "Copying Linux Path" - $MBSIGN_APPFOLDER = '$(MBSIGN_APPFOLDER)' - $MBSIGN_APPFOLDER = ($MBSIGN_APPFOLDER -replace '/build', '') - - $versionRegex = '\d+\.\d+\.\d+' - $package = Get-ChildItem -Path $MBSIGN_APPFOLDER -Directory | - Where-Object { $_.Name -match $versionRegex } - - if ($package.Count -ne 1) { - Write-Host "There should be exactly one matching subfolder, but found $($package.Count)." - exit 1 - } - - $MBSIGN_APPFOLDER = $package[0].FullName + '/build' - $MBSIGN_APPFOLDER | Write-Host - $SignConfigPath = $MBSIGN_APPFOLDER + '/signconfig.xml' - Copy-Item -Path "$(MBSIGN_APPFOLDER)/signconfig.xml" -Destination $SignConfigPath -Force - condition: and(succeeded(), eq(variables['Agent.OS'], 'Linux')) + signType: $(_SignType) + zipSources: false + feedSource: https://dnceng.pkgs.visualstudio.com/_packaging/MicroBuildToolset/nuget/v3/index.json + ${{ if eq(parameters.microbuildUseESRP, true) }}: + ConnectedServiceName: 'MicroBuild Signing Task (DevDiv)' + ${{ if eq(variables['System.TeamProject'], 'DevDiv') }}: + ConnectedPMEServiceName: beb8cb23-b303-4c95-ab26-9e44bc958d39 + ${{ else }}: + ConnectedPMEServiceName: c24de2a5-cc7a-493d-95e4-8e5ff5cad2bc + env: + TeamName: $(_TeamName) + MicroBuildOutputFolderOverride: ${{ parameters.microBuildOutputFolder }} + SYSTEM_ACCESSTOKEN: $(System.AccessToken) + continueOnError: ${{ parameters.continueOnError }} + condition: and(succeeded(), ne(variables['Agent.Os'], 'Windows_NT'), eq(variables['_SignType'], 'real')) diff --git a/eng/common/core-templates/steps/publish-logs.yml b/eng/common/core-templates/steps/publish-logs.yml index de24d0087c..10f825e270 100644 --- a/eng/common/core-templates/steps/publish-logs.yml +++ b/eng/common/core-templates/steps/publish-logs.yml @@ -12,22 +12,22 @@ steps: inputs: targetType: inline script: | - New-Item -ItemType Directory $(Build.SourcesDirectory)/PostBuildLogs/${{parameters.StageLabel}}/${{parameters.JobLabel}}/ - Move-Item -Path $(Build.SourcesDirectory)/artifacts/log/Debug/* $(Build.SourcesDirectory)/PostBuildLogs/${{parameters.StageLabel}}/${{parameters.JobLabel}}/ + New-Item -ItemType Directory $(System.DefaultWorkingDirectory)/PostBuildLogs/${{parameters.StageLabel}}/${{parameters.JobLabel}}/ + Move-Item -Path $(System.DefaultWorkingDirectory)/artifacts/log/Debug/* $(System.DefaultWorkingDirectory)/PostBuildLogs/${{parameters.StageLabel}}/${{parameters.JobLabel}}/ continueOnError: true condition: always() - task: PowerShell@2 displayName: Redact Logs inputs: - filePath: $(Build.SourcesDirectory)/eng/common/post-build/redact-logs.ps1 + filePath: $(System.DefaultWorkingDirectory)/eng/common/post-build/redact-logs.ps1 # For now this needs to have explicit list of all sensitive data. Taken from eng/publishing/v3/publish.yml - # Sensitive data can as well be added to $(Build.SourcesDirectory)/eng/BinlogSecretsRedactionFile.txt' + # Sensitive data can as well be added to $(System.DefaultWorkingDirectory)/eng/BinlogSecretsRedactionFile.txt' # If the file exists - sensitive data for redaction will be sourced from it # (single entry per line, lines starting with '# ' are considered comments and skipped) - arguments: -InputPath '$(Build.SourcesDirectory)/PostBuildLogs' + arguments: -InputPath '$(System.DefaultWorkingDirectory)/PostBuildLogs' -BinlogToolVersion ${{parameters.BinlogToolVersion}} - -TokensFilePath '$(Build.SourcesDirectory)/eng/BinlogSecretsRedactionFile.txt' + -TokensFilePath '$(System.DefaultWorkingDirectory)/eng/BinlogSecretsRedactionFile.txt' '$(publishing-dnceng-devdiv-code-r-build-re)' '$(MaestroAccessToken)' '$(dn-bot-all-orgs-artifact-feeds-rw)' @@ -44,7 +44,7 @@ steps: - task: CopyFiles@2 displayName: Gather post build logs inputs: - SourceFolder: '$(Build.SourcesDirectory)/PostBuildLogs' + SourceFolder: '$(System.DefaultWorkingDirectory)/PostBuildLogs' Contents: '**' TargetFolder: '$(Build.ArtifactStagingDirectory)/PostBuildLogs' condition: always() diff --git a/eng/common/core-templates/steps/source-build.yml b/eng/common/core-templates/steps/source-build.yml index c6b9ef51ac..acf16ed349 100644 --- a/eng/common/core-templates/steps/source-build.yml +++ b/eng/common/core-templates/steps/source-build.yml @@ -19,19 +19,6 @@ steps: set -x df -h - # If file changes are detected, set CopyWipIntoInnerSourceBuildRepo to copy the WIP changes into the inner source build repo. - internalRestoreArgs= - if ! git diff --quiet; then - internalRestoreArgs='/p:CopyWipIntoInnerSourceBuildRepo=true' - # The 'Copy WIP' feature of source build uses git stash to apply changes from the original repo. - # This only works if there is a username/email configured, which won't be the case in most CI runs. - git config --get user.email - if [ $? -ne 0 ]; then - git config user.email dn-bot@microsoft.com - git config user.name dn-bot - fi - fi - # If building on the internal project, the internal storage variable may be available (usually only if needed) # In that case, add variables to allow the download of internal runtimes if the specified versions are not found # in the default public locations. @@ -46,36 +33,11 @@ steps: buildConfig='$(_BuildConfig)' fi - officialBuildArgs= - if [ '${{ and(ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest')) }}' = 'True' ]; then - officialBuildArgs='/p:DotNetPublishUsingPipelines=true /p:OfficialBuildId=$(BUILD.BUILDNUMBER)' - fi - targetRidArgs= if [ '${{ parameters.platform.targetRID }}' != '' ]; then targetRidArgs='/p:TargetRid=${{ parameters.platform.targetRID }}' fi - runtimeOsArgs= - if [ '${{ parameters.platform.runtimeOS }}' != '' ]; then - runtimeOsArgs='/p:RuntimeOS=${{ parameters.platform.runtimeOS }}' - fi - - baseOsArgs= - if [ '${{ parameters.platform.baseOS }}' != '' ]; then - baseOsArgs='/p:BaseOS=${{ parameters.platform.baseOS }}' - fi - - publishArgs= - if [ '${{ parameters.platform.skipPublishValidation }}' != 'true' ]; then - publishArgs='--publish' - fi - - assetManifestFileName=SourceBuild_RidSpecific.xml - if [ '${{ parameters.platform.name }}' != '' ]; then - assetManifestFileName=SourceBuild_${{ parameters.platform.name }}.xml - fi - portableBuildArgs= if [ '${{ parameters.platform.portableBuild }}' != '' ]; then portableBuildArgs='/p:PortableBuild=${{ parameters.platform.portableBuild }}' @@ -83,40 +45,20 @@ steps: ${{ coalesce(parameters.platform.buildScript, './build.sh') }} --ci \ --configuration $buildConfig \ - --restore --build --pack $publishArgs -bl \ + --restore --build --pack -bl \ + --source-build \ ${{ parameters.platform.buildArguments }} \ - $officialBuildArgs \ $internalRuntimeDownloadArgs \ - $internalRestoreArgs \ $targetRidArgs \ - $runtimeOsArgs \ - $baseOsArgs \ $portableBuildArgs \ - /p:DotNetBuildSourceOnly=true \ - /p:DotNetBuildRepo=true \ - /p:AssetManifestFileName=$assetManifestFileName displayName: Build -# Upload build logs for diagnosis. -- task: CopyFiles@2 - displayName: Prepare BuildLogs staging directory - inputs: - SourceFolder: '$(Build.SourcesDirectory)' - Contents: | - **/*.log - **/*.binlog - artifacts/sb/prebuilt-report/** - TargetFolder: '$(Build.StagingDirectory)/BuildLogs' - CleanTargetFolder: true - continueOnError: true - condition: succeededOrFailed() - - template: /eng/common/core-templates/steps/publish-pipeline-artifacts.yml parameters: is1ESPipeline: ${{ parameters.is1ESPipeline }} args: displayName: Publish BuildLogs - targetPath: '$(Build.StagingDirectory)/BuildLogs' + targetPath: artifacts/log/${{ coalesce(variables._BuildConfig, 'Release') }} artifactName: BuildLogs_SourceBuild_${{ parameters.platform.name }}_Attempt$(System.JobAttempt) continueOnError: true condition: succeededOrFailed() diff --git a/eng/common/core-templates/steps/source-index-stage1-publish.yml b/eng/common/core-templates/steps/source-index-stage1-publish.yml index 99c2326fc1..eff4573c6e 100644 --- a/eng/common/core-templates/steps/source-index-stage1-publish.yml +++ b/eng/common/core-templates/steps/source-index-stage1-publish.yml @@ -1,15 +1,15 @@ parameters: - sourceIndexUploadPackageVersion: 2.0.0-20250425.2 - sourceIndexProcessBinlogPackageVersion: 1.0.1-20250425.2 + sourceIndexUploadPackageVersion: 2.0.0-20250906.1 + sourceIndexProcessBinlogPackageVersion: 1.0.1-20250906.1 sourceIndexPackageSource: https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-tools/nuget/v3/index.json binlogPath: artifacts/log/Debug/Build.binlog steps: - task: UseDotNet@2 - displayName: "Source Index: Use .NET 8 SDK" + displayName: "Source Index: Use .NET 9 SDK" inputs: packageType: sdk - version: 8.0.x + version: 9.0.x installationPath: $(Agent.TempDirectory)/dotnet workingDirectory: $(Agent.TempDirectory) @@ -20,7 +20,7 @@ steps: # Set working directory to temp directory so 'dotnet' doesn't try to use global.json and use the repo's sdk. workingDirectory: $(Agent.TempDirectory) -- script: $(Agent.TempDirectory)/.source-index/tools/BinLogToSln -i ${{parameters.BinlogPath}} -r $(Build.SourcesDirectory) -n $(Build.Repository.Name) -o .source-index/stage1output +- script: $(Agent.TempDirectory)/.source-index/tools/BinLogToSln -i ${{parameters.BinlogPath}} -r $(System.DefaultWorkingDirectory) -n $(Build.Repository.Name) -o .source-index/stage1output displayName: "Source Index: Process Binlog into indexable sln" - ${{ if and(ne(parameters.runAsPublic, 'true'), ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest')) }}: diff --git a/eng/common/cross/build-rootfs.sh b/eng/common/cross/build-rootfs.sh index d6f005b5da..8abfb71f72 100644 --- a/eng/common/cross/build-rootfs.sh +++ b/eng/common/cross/build-rootfs.sh @@ -295,8 +295,8 @@ while :; do ;; noble) # Ubuntu 24.04 __CodeName=noble - if [[ -n "$__LLDB_Package" ]]; then - __LLDB_Package="liblldb-18-dev" + if [[ -z "$__LLDB_Package" ]]; then + __LLDB_Package="liblldb-19-dev" fi ;; stretch) # Debian 9 diff --git a/eng/common/darc-init.sh b/eng/common/darc-init.sh index 36dbd45e1c..9f5ad6b763 100644 --- a/eng/common/darc-init.sh +++ b/eng/common/darc-init.sh @@ -5,7 +5,7 @@ darcVersion='' versionEndpoint='https://maestro.dot.net/api/assets/darc-version?api-version=2020-02-20' verbosity='minimal' -while [[ $# > 0 ]]; do +while [[ $# -gt 0 ]]; do opt="$(echo "$1" | tr "[:upper:]" "[:lower:]")" case "$opt" in --darcversion) @@ -68,7 +68,7 @@ function InstallDarcCli { fi fi - local arcadeServicesSource="https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-tools/nuget/v3/index.json" + local arcadeServicesSource="https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-eng/nuget/v3/index.json" echo "Installing Darc CLI version $darcVersion..." echo "You may need to restart your command shell if this is the first dotnet tool you have installed." diff --git a/eng/common/dotnet-install.sh b/eng/common/dotnet-install.sh index 7b9d97e3bd..61f302bb67 100755 --- a/eng/common/dotnet-install.sh +++ b/eng/common/dotnet-install.sh @@ -18,7 +18,7 @@ architecture='' runtime='dotnet' runtimeSourceFeed='' runtimeSourceFeedKey='' -while [[ $# > 0 ]]; do +while [[ $# -gt 0 ]]; do opt="$(echo "$1" | tr "[:upper:]" "[:lower:]")" case "$opt" in -version|-v) diff --git a/eng/common/dotnet.cmd b/eng/common/dotnet.cmd new file mode 100644 index 0000000000..527fa4bb38 --- /dev/null +++ b/eng/common/dotnet.cmd @@ -0,0 +1,7 @@ +@echo off + +:: This script is used to install the .NET SDK. +:: It will also invoke the SDK with any provided arguments. + +powershell -ExecutionPolicy ByPass -NoProfile -command "& """%~dp0dotnet.ps1""" %*" +exit /b %ErrorLevel% diff --git a/eng/common/dotnet.ps1 b/eng/common/dotnet.ps1 new file mode 100644 index 0000000000..45e5676c9e --- /dev/null +++ b/eng/common/dotnet.ps1 @@ -0,0 +1,11 @@ +# This script is used to install the .NET SDK. +# It will also invoke the SDK with any provided arguments. + +. $PSScriptRoot\tools.ps1 +$dotnetRoot = InitializeDotNetCli -install:$true + +# Invoke acquired SDK with args if they are provided +if ($args.count -gt 0) { + $env:DOTNET_NOLOGO=1 + & "$dotnetRoot\dotnet.exe" $args +} diff --git a/eng/common/dotnet.sh b/eng/common/dotnet.sh new file mode 100644 index 0000000000..f6d24871c1 --- /dev/null +++ b/eng/common/dotnet.sh @@ -0,0 +1,26 @@ +#!/usr/bin/env bash + +# This script is used to install the .NET SDK. +# It will also invoke the SDK with any provided arguments. + +source="${BASH_SOURCE[0]}" +# resolve $SOURCE until the file is no longer a symlink +while [[ -h $source ]]; do + scriptroot="$( cd -P "$( dirname "$source" )" && pwd )" + source="$(readlink "$source")" + + # if $source was a relative symlink, we need to resolve it relative to the path where the + # symlink file was located + [[ $source != /* ]] && source="$scriptroot/$source" +done +scriptroot="$( cd -P "$( dirname "$source" )" && pwd )" + +source $scriptroot/tools.sh +InitializeDotNetCli true # install + +# Invoke acquired SDK with args if they are provided +if [[ $# -gt 0 ]]; then + __dotnetDir=${_InitializeDotNetCli} + dotnetPath=${__dotnetDir}/dotnet + ${dotnetPath} "$@" +fi diff --git a/eng/common/generate-locproject.ps1 b/eng/common/generate-locproject.ps1 index 524aaa57f2..fa1cdc2b30 100644 --- a/eng/common/generate-locproject.ps1 +++ b/eng/common/generate-locproject.ps1 @@ -33,15 +33,27 @@ $jsonTemplateFiles | ForEach-Object { $jsonWinformsTemplateFiles = Get-ChildItem -Recurse -Path "$SourcesDirectory" | Where-Object { $_.FullName -Match "en\\strings\.json" } # current winforms pattern +$wxlFilesV3 = @() +$wxlFilesV5 = @() $wxlFiles = Get-ChildItem -Recurse -Path "$SourcesDirectory" | Where-Object { $_.FullName -Match "\\.+\.wxl" -And -Not( $_.Directory.Name -Match "\d{4}" ) } # localized files live in four digit lang ID directories; this excludes them if (-not $wxlFiles) { $wxlEnFiles = Get-ChildItem -Recurse -Path "$SourcesDirectory" | Where-Object { $_.FullName -Match "\\1033\\.+\.wxl" } # pick up en files (1033 = en) specifically so we can copy them to use as the neutral xlf files if ($wxlEnFiles) { - $wxlFiles = @() - $wxlEnFiles | ForEach-Object { - $destinationFile = "$($_.Directory.Parent.FullName)\$($_.Name)" - $wxlFiles += Copy-Item "$($_.FullName)" -Destination $destinationFile -PassThru - } + $wxlFiles = @() + $wxlEnFiles | ForEach-Object { + $destinationFile = "$($_.Directory.Parent.FullName)\$($_.Name)" + $content = Get-Content $_.FullName -Raw + + # Split files on schema to select different parser settings in the generated project. + if ($content -like "*http://wixtoolset.org/schemas/v4/wxl*") + { + $wxlFilesV5 += Copy-Item $_.FullName -Destination $destinationFile -PassThru + } + elseif ($content -like "*http://schemas.microsoft.com/wix/2006/localization*") + { + $wxlFilesV3 += Copy-Item $_.FullName -Destination $destinationFile -PassThru + } + } } } @@ -114,7 +126,32 @@ $locJson = @{ CloneLanguageSet = "WiX_CloneLanguages" LssFiles = @( "wxl_loc.lss" ) LocItems = @( - $wxlFiles | ForEach-Object { + $wxlFilesV3 | ForEach-Object { + $outputPath = "$($_.Directory.FullName | Resolve-Path -Relative)\" + $continue = $true + foreach ($exclusion in $exclusions.Exclusions) { + if ($_.FullName.Contains($exclusion)) { + $continue = $false + } + } + $sourceFile = ($_.FullName | Resolve-Path -Relative) + if ($continue) + { + return @{ + SourceFile = $sourceFile + CopyOption = "LangIDOnPath" + OutputPath = $outputPath + } + } + } + ) + }, + @{ + LanguageSet = $LanguageSet + CloneLanguageSet = "WiX_CloneLanguages" + LssFiles = @( "P210WxlSchemaV4.lss" ) + LocItems = @( + $wxlFilesV5 | ForEach-Object { $outputPath = "$($_.Directory.FullName | Resolve-Path -Relative)\" $continue = $true foreach ($exclusion in $exclusions.Exclusions) { diff --git a/eng/common/internal-feed-operations.sh b/eng/common/internal-feed-operations.sh index 9378223ba0..6299e7effd 100644 --- a/eng/common/internal-feed-operations.sh +++ b/eng/common/internal-feed-operations.sh @@ -100,7 +100,7 @@ operation='' authToken='' repoName='' -while [[ $# > 0 ]]; do +while [[ $# -gt 0 ]]; do opt="$(echo "$1" | tr "[:upper:]" "[:lower:]")" case "$opt" in --operation) diff --git a/eng/common/internal/NuGet.config b/eng/common/internal/NuGet.config index 19d3d311b1..f70261ed68 100644 --- a/eng/common/internal/NuGet.config +++ b/eng/common/internal/NuGet.config @@ -4,4 +4,7 @@ + + + diff --git a/eng/common/post-build/nuget-verification.ps1 b/eng/common/post-build/nuget-verification.ps1 index a365194a93..ac5c69ffca 100644 --- a/eng/common/post-build/nuget-verification.ps1 +++ b/eng/common/post-build/nuget-verification.ps1 @@ -30,7 +30,7 @@ [CmdletBinding(PositionalBinding = $false)] param( [string]$NuGetExePath, - [string]$PackageSource = "https://api.nuget.org/v3/index.json", + [string]$PackageSource = "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public/nuget/v3/index.json", [string]$DownloadPath, [Parameter(ValueFromRemainingArguments = $true)] [string[]]$args diff --git a/eng/common/sdk-task.ps1 b/eng/common/sdk-task.ps1 index a9d2a2d269..4655af7a2d 100644 --- a/eng/common/sdk-task.ps1 +++ b/eng/common/sdk-task.ps1 @@ -7,13 +7,14 @@ Param( [switch] $restore, [switch] $prepareMachine, [switch][Alias('nobl')]$excludeCIBinaryLog, + [switch]$noWarnAsError, [switch] $help, [Parameter(ValueFromRemainingArguments=$true)][String[]]$properties ) $ci = $true $binaryLog = if ($excludeCIBinaryLog) { $false } else { $true } -$warnAsError = $true +$warnAsError = if ($noWarnAsError) { $false } else { $true } . $PSScriptRoot\tools.ps1 @@ -67,7 +68,7 @@ try { $GlobalJson.tools | Add-Member -Name "vs" -Value (ConvertFrom-Json "{ `"version`": `"16.5`" }") -MemberType NoteProperty } if( -not ($GlobalJson.tools.PSObject.Properties.Name -match "xcopy-msbuild" )) { - $GlobalJson.tools | Add-Member -Name "xcopy-msbuild" -Value "17.13.0" -MemberType NoteProperty + $GlobalJson.tools | Add-Member -Name "xcopy-msbuild" -Value "17.14.16" -MemberType NoteProperty } if ($GlobalJson.tools."xcopy-msbuild".Trim() -ine "none") { $xcopyMSBuildToolsFolder = InitializeXCopyMSBuild $GlobalJson.tools."xcopy-msbuild" -install $true diff --git a/eng/common/sdk-task.sh b/eng/common/sdk-task.sh index 2f83adc026..3270f83fa9 100644 --- a/eng/common/sdk-task.sh +++ b/eng/common/sdk-task.sh @@ -10,6 +10,7 @@ show_usage() { echo "Advanced settings:" echo " --excludeCIBinarylog Don't output binary log (short: -nobl)" + echo " --noWarnAsError Do not warn as error" echo "" echo "Command line arguments not listed above are passed thru to msbuild." } @@ -52,6 +53,7 @@ exclude_ci_binary_log=false restore=false help=false properties='' +warnAsError=true while (($# > 0)); do lowerI="$(echo $1 | tr "[:upper:]" "[:lower:]")" @@ -73,6 +75,10 @@ while (($# > 0)); do exclude_ci_binary_log=true shift 1 ;; + --noWarnAsError) + warnAsError=false + shift 1 + ;; --help) help=true shift 1 @@ -85,7 +91,6 @@ while (($# > 0)); do done ci=true -warnAsError=true if $help; then show_usage diff --git a/eng/common/template-guidance.md b/eng/common/template-guidance.md index 98bbc1ded0..4bf4cf41bd 100644 --- a/eng/common/template-guidance.md +++ b/eng/common/template-guidance.md @@ -50,7 +50,7 @@ extends: - task: CopyFiles@2 displayName: Gather build output inputs: - SourceFolder: '$(Build.SourcesDirectory)/artifacts/marvel' + SourceFolder: '$(System.DefaultWorkingDirectory)/artifacts/marvel' Contents: '**' TargetFolder: '$(Build.ArtifactStagingDirectory)/artifacts/marvel' ``` diff --git a/eng/common/templates-official/job/job.yml b/eng/common/templates-official/job/job.yml index a8a9432874..92a0664f56 100644 --- a/eng/common/templates-official/job/job.yml +++ b/eng/common/templates-official/job/job.yml @@ -3,7 +3,7 @@ parameters: enableSbom: true runAsPublic: false PackageVersion: 9.0.0 - BuildDropPath: '$(Build.SourcesDirectory)/artifacts' + BuildDropPath: '$(System.DefaultWorkingDirectory)/artifacts' jobs: - template: /eng/common/core-templates/job/job.yml diff --git a/eng/common/templates-official/variables/sdl-variables.yml b/eng/common/templates-official/variables/sdl-variables.yml index dbdd66d4a4..f1311bbb1b 100644 --- a/eng/common/templates-official/variables/sdl-variables.yml +++ b/eng/common/templates-official/variables/sdl-variables.yml @@ -4,4 +4,4 @@ variables: - name: DefaultGuardianVersion value: 0.109.0 - name: GuardianPackagesConfigFile - value: $(Build.SourcesDirectory)\eng\common\sdl\packages.config \ No newline at end of file + value: $(System.DefaultWorkingDirectory)\eng\common\sdl\packages.config \ No newline at end of file diff --git a/eng/common/templates/job/job.yml b/eng/common/templates/job/job.yml index 7cbf668c22..238fa0818f 100644 --- a/eng/common/templates/job/job.yml +++ b/eng/common/templates/job/job.yml @@ -6,7 +6,7 @@ parameters: enableSbom: true runAsPublic: false PackageVersion: 9.0.0 - BuildDropPath: '$(Build.SourcesDirectory)/artifacts' + BuildDropPath: '$(System.DefaultWorkingDirectory)/artifacts' jobs: - template: /eng/common/core-templates/job/job.yml @@ -77,7 +77,7 @@ jobs: parameters: is1ESPipeline: false args: - targetPath: '$(Build.SourcesDirectory)\eng\common\BuildConfiguration' + targetPath: '$(System.DefaultWorkingDirectory)\eng\common\BuildConfiguration' artifactName: 'BuildConfiguration' displayName: 'Publish build retry configuration' continueOnError: true diff --git a/eng/common/templates/steps/vmr-sync.yml b/eng/common/templates/steps/vmr-sync.yml new file mode 100644 index 0000000000..599afb6186 --- /dev/null +++ b/eng/common/templates/steps/vmr-sync.yml @@ -0,0 +1,207 @@ +### These steps synchronize new code from product repositories into the VMR (https://github.com/dotnet/dotnet). +### They initialize the darc CLI and pull the new updates. +### Changes are applied locally onto the already cloned VMR (located in $vmrPath). + +parameters: +- name: targetRef + displayName: Target revision in dotnet/ to synchronize + type: string + default: $(Build.SourceVersion) + +- name: vmrPath + displayName: Path where the dotnet/dotnet is checked out to + type: string + default: $(Agent.BuildDirectory)/vmr + +- name: additionalSyncs + displayName: Optional list of package names whose repo's source will also be synchronized in the local VMR, e.g. NuGet.Protocol + type: object + default: [] + +steps: +- checkout: vmr + displayName: Clone dotnet/dotnet + path: vmr + clean: true + +- checkout: self + displayName: Clone $(Build.Repository.Name) + path: repo + fetchDepth: 0 + +# This step is needed so that when we get a detached HEAD / shallow clone, +# we still pull the commit into the temporary repo clone to use it during the sync. +# Also unshallow the clone so that forwardflow command would work. +- script: | + git branch repo-head + git rev-parse HEAD + displayName: Label PR commit + workingDirectory: $(Agent.BuildDirectory)/repo + +- script: | + vmr_sha=$(grep -oP '(?<=Sha=")[^"]*' $(Agent.BuildDirectory)/repo/eng/Version.Details.xml) + echo "##vso[task.setvariable variable=vmr_sha]$vmr_sha" + displayName: Obtain the vmr sha from Version.Details.xml (Unix) + condition: ne(variables['Agent.OS'], 'Windows_NT') + workingDirectory: $(Agent.BuildDirectory)/repo + +- powershell: | + [xml]$xml = Get-Content -Path $(Agent.BuildDirectory)/repo/eng/Version.Details.xml + $vmr_sha = $xml.SelectSingleNode("//Source").Sha + Write-Output "##vso[task.setvariable variable=vmr_sha]$vmr_sha" + displayName: Obtain the vmr sha from Version.Details.xml (Windows) + condition: eq(variables['Agent.OS'], 'Windows_NT') + workingDirectory: $(Agent.BuildDirectory)/repo + +- script: | + git fetch --all + git checkout $(vmr_sha) + displayName: Checkout VMR at correct sha for repo flow + workingDirectory: ${{ parameters.vmrPath }} + +- script: | + git config --global user.name "dotnet-maestro[bot]" + git config --global user.email "dotnet-maestro[bot]@users.noreply.github.com" + displayName: Set git author to dotnet-maestro[bot] + workingDirectory: ${{ parameters.vmrPath }} + +- script: | + ./eng/common/vmr-sync.sh \ + --vmr ${{ parameters.vmrPath }} \ + --tmp $(Agent.TempDirectory) \ + --azdev-pat '$(dn-bot-all-orgs-code-r)' \ + --ci \ + --debug + + if [ "$?" -ne 0 ]; then + echo "##vso[task.logissue type=error]Failed to synchronize the VMR" + exit 1 + fi + displayName: Sync repo into VMR (Unix) + condition: ne(variables['Agent.OS'], 'Windows_NT') + workingDirectory: $(Agent.BuildDirectory)/repo + +- script: | + git config --global diff.astextplain.textconv echo + git config --system core.longpaths true + displayName: Configure Windows git (longpaths, astextplain) + condition: eq(variables['Agent.OS'], 'Windows_NT') + +- powershell: | + ./eng/common/vmr-sync.ps1 ` + -vmr ${{ parameters.vmrPath }} ` + -tmp $(Agent.TempDirectory) ` + -azdevPat '$(dn-bot-all-orgs-code-r)' ` + -ci ` + -debugOutput + + if ($LASTEXITCODE -ne 0) { + echo "##vso[task.logissue type=error]Failed to synchronize the VMR" + exit 1 + } + displayName: Sync repo into VMR (Windows) + condition: eq(variables['Agent.OS'], 'Windows_NT') + workingDirectory: $(Agent.BuildDirectory)/repo + +- ${{ if eq(variables['Build.Reason'], 'PullRequest') }}: + - task: CopyFiles@2 + displayName: Collect failed patches + condition: failed() + inputs: + SourceFolder: '$(Agent.TempDirectory)' + Contents: '*.patch' + TargetFolder: '$(Build.ArtifactStagingDirectory)/FailedPatches' + + - publish: '$(Build.ArtifactStagingDirectory)/FailedPatches' + artifact: $(System.JobDisplayName)_FailedPatches + displayName: Upload failed patches + condition: failed() + +- ${{ each assetName in parameters.additionalSyncs }}: + # The vmr-sync script ends up staging files in the local VMR so we have to commit those + - script: + git commit --allow-empty -am "Forward-flow $(Build.Repository.Name)" + displayName: Commit local VMR changes + workingDirectory: ${{ parameters.vmrPath }} + + - script: | + set -ex + + echo "Searching for details of asset ${{ assetName }}..." + + # Use darc to get dependencies information + dependencies=$(./.dotnet/dotnet darc get-dependencies --name '${{ assetName }}' --ci) + + # Extract repository URL and commit hash + repository=$(echo "$dependencies" | grep 'Repo:' | sed 's/Repo:[[:space:]]*//' | head -1) + + if [ -z "$repository" ]; then + echo "##vso[task.logissue type=error]Asset ${{ assetName }} not found in the dependency list" + exit 1 + fi + + commit=$(echo "$dependencies" | grep 'Commit:' | sed 's/Commit:[[:space:]]*//' | head -1) + + echo "Updating the VMR from $repository / $commit..." + cd .. + git clone $repository ${{ assetName }} + cd ${{ assetName }} + git checkout $commit + git branch "sync/$commit" + + ./eng/common/vmr-sync.sh \ + --vmr ${{ parameters.vmrPath }} \ + --tmp $(Agent.TempDirectory) \ + --azdev-pat '$(dn-bot-all-orgs-code-r)' \ + --ci \ + --debug + + if [ "$?" -ne 0 ]; then + echo "##vso[task.logissue type=error]Failed to synchronize the VMR" + exit 1 + fi + displayName: Sync ${{ assetName }} into (Unix) + condition: ne(variables['Agent.OS'], 'Windows_NT') + workingDirectory: $(Agent.BuildDirectory)/repo + + - powershell: | + $ErrorActionPreference = 'Stop' + + Write-Host "Searching for details of asset ${{ assetName }}..." + + $dependencies = .\.dotnet\dotnet darc get-dependencies --name '${{ assetName }}' --ci + + $repository = $dependencies | Select-String -Pattern 'Repo:\s+([^\s]+)' | Select-Object -First 1 + $repository -match 'Repo:\s+([^\s]+)' | Out-Null + $repository = $matches[1] + + if ($repository -eq $null) { + Write-Error "Asset ${{ assetName }} not found in the dependency list" + exit 1 + } + + $commit = $dependencies | Select-String -Pattern 'Commit:\s+([^\s]+)' | Select-Object -First 1 + $commit -match 'Commit:\s+([^\s]+)' | Out-Null + $commit = $matches[1] + + Write-Host "Updating the VMR from $repository / $commit..." + cd .. + git clone $repository ${{ assetName }} + cd ${{ assetName }} + git checkout $commit + git branch "sync/$commit" + + .\eng\common\vmr-sync.ps1 ` + -vmr ${{ parameters.vmrPath }} ` + -tmp $(Agent.TempDirectory) ` + -azdevPat '$(dn-bot-all-orgs-code-r)' ` + -ci ` + -debugOutput + + if ($LASTEXITCODE -ne 0) { + echo "##vso[task.logissue type=error]Failed to synchronize the VMR" + exit 1 + } + displayName: Sync ${{ assetName }} into (Windows) + condition: ne(variables['Agent.OS'], 'Windows_NT') + workingDirectory: $(Agent.BuildDirectory)/repo diff --git a/eng/common/templates/vmr-build-pr.yml b/eng/common/templates/vmr-build-pr.yml new file mode 100644 index 0000000000..ce3c29a62f --- /dev/null +++ b/eng/common/templates/vmr-build-pr.yml @@ -0,0 +1,42 @@ +# This pipeline is used for running the VMR verification of the PR changes in repo-level PRs. +# +# It will run a full set of verification jobs defined in: +# https://github.com/dotnet/dotnet/blob/10060d128e3f470e77265f8490f5e4f72dae738e/eng/pipelines/templates/stages/vmr-build.yml#L27-L38 +# +# For repos that do not need to run the full set, you would do the following: +# +# 1. Copy this YML file to a repo-specific location, i.e. outside of eng/common. +# +# 2. Add `verifications` parameter to VMR template reference +# +# Examples: +# - For source-build stage 1 verification, add the following: +# verifications: [ "source-build-stage1" ] +# +# - For Windows only verifications, add the following: +# verifications: [ "unified-build-windows-x64", "unified-build-windows-x86" ] + +trigger: none +pr: none + +variables: +- template: /eng/common/templates/variables/pool-providers.yml@self + +- name: skipComponentGovernanceDetection # we run CG on internal builds only + value: true + +- name: Codeql.Enabled # we run CodeQL on internal builds only + value: false + +resources: + repositories: + - repository: vmr + type: github + name: dotnet/dotnet + endpoint: dotnet + +stages: +- template: /eng/pipelines/templates/stages/vmr-build.yml@vmr + parameters: + isBuiltFromVmr: false + scope: lite diff --git a/eng/common/tools.ps1 b/eng/common/tools.ps1 index 7373e53054..4bc50bd568 100644 --- a/eng/common/tools.ps1 +++ b/eng/common/tools.ps1 @@ -65,10 +65,8 @@ $ErrorActionPreference = 'Stop' # Base-64 encoded SAS token that has permission to storage container described by $runtimeSourceFeed [string]$runtimeSourceFeedKey = if (Test-Path variable:runtimeSourceFeedKey) { $runtimeSourceFeedKey } else { $null } -# True if the build is a product build -[bool]$productBuild = if (Test-Path variable:productBuild) { $productBuild } else { $false } - -[String[]]$properties = if (Test-Path variable:properties) { $properties } else { @() } +# True when the build is running within the VMR. +[bool]$fromVMR = if (Test-Path variable:fromVMR) { $fromVMR } else { $false } function Create-Directory ([string[]] $path) { New-Item -Path $path -Force -ItemType 'Directory' | Out-Null @@ -259,7 +257,20 @@ function Retry($downloadBlock, $maxRetries = 5) { function GetDotNetInstallScript([string] $dotnetRoot) { $installScript = Join-Path $dotnetRoot 'dotnet-install.ps1' + $shouldDownload = $false + if (!(Test-Path $installScript)) { + $shouldDownload = $true + } else { + # Check if the script is older than 30 days + $fileAge = (Get-Date) - (Get-Item $installScript).LastWriteTime + if ($fileAge.Days -gt 30) { + Write-Host "Existing install script is too old, re-downloading..." + $shouldDownload = $true + } + } + + if ($shouldDownload) { Create-Directory $dotnetRoot $ProgressPreference = 'SilentlyContinue' # Don't display the console progress UI - it's a huge perf hit $uri = "https://builds.dotnet.microsoft.com/dotnet/scripts/$dotnetInstallScriptVersion/dotnet-install.ps1" @@ -383,8 +394,8 @@ function InitializeVisualStudioMSBuild([bool]$install, [object]$vsRequirements = # If the version of msbuild is going to be xcopied, # use this version. Version matches a package here: - # https://dev.azure.com/dnceng/public/_artifacts/feed/dotnet-eng/NuGet/Microsoft.DotNet.Arcade.MSBuild.Xcopy/versions/17.13.0 - $defaultXCopyMSBuildVersion = '17.13.0' + # https://dev.azure.com/dnceng/public/_artifacts/feed/dotnet-eng/NuGet/Microsoft.DotNet.Arcade.MSBuild.Xcopy/versions/17.14.16 + $defaultXCopyMSBuildVersion = '17.14.16' if (!$vsRequirements) { if (Get-Member -InputObject $GlobalJson.tools -Name 'vs') { @@ -416,7 +427,7 @@ function InitializeVisualStudioMSBuild([bool]$install, [object]$vsRequirements = # Locate Visual Studio installation or download x-copy msbuild. $vsInfo = LocateVisualStudio $vsRequirements - if ($vsInfo -ne $null) { + if ($vsInfo -ne $null -and $env:ForceUseXCopyMSBuild -eq $null) { # Ensure vsInstallDir has a trailing slash $vsInstallDir = Join-Path $vsInfo.installationPath "\" $vsMajorVersion = $vsInfo.installationVersion.Split('.')[0] @@ -533,7 +544,8 @@ function LocateVisualStudio([object]$vsRequirements = $null){ if (Get-Member -InputObject $GlobalJson.tools -Name 'vswhere') { $vswhereVersion = $GlobalJson.tools.vswhere } else { - $vswhereVersion = '2.5.2' + # keep this in sync with the VSWhereVersion in DefaultVersions.props + $vswhereVersion = '3.1.7' } $vsWhereDir = Join-Path $ToolsDir "vswhere\$vswhereVersion" @@ -541,7 +553,8 @@ function LocateVisualStudio([object]$vsRequirements = $null){ if (!(Test-Path $vsWhereExe)) { Create-Directory $vsWhereDir - Write-Host 'Downloading vswhere' + Write-Host "Downloading vswhere $vswhereVersion" + $ProgressPreference = 'SilentlyContinue' # Don't display the console progress UI - it's a huge perf hit Retry({ Invoke-WebRequest "https://netcorenativeassets.blob.core.windows.net/resource-packages/external/windows/vswhere/$vswhereVersion/vswhere.exe" -OutFile $vswhereExe }) @@ -646,7 +659,6 @@ function GetNuGetPackageCachePath() { $env:NUGET_PACKAGES = Join-Path $env:UserProfile '.nuget\packages\' } else { $env:NUGET_PACKAGES = Join-Path $RepoRoot '.packages\' - $env:RESTORENOHTTPCACHE = $true } } @@ -768,28 +780,13 @@ function MSBuild() { $toolsetBuildProject = InitializeToolset $basePath = Split-Path -parent $toolsetBuildProject - $possiblePaths = @( - # new scripts need to work with old packages, so we need to look for the old names/versions - (Join-Path $basePath (Join-Path $buildTool.Framework 'Microsoft.DotNet.ArcadeLogging.dll')), - (Join-Path $basePath (Join-Path $buildTool.Framework 'Microsoft.DotNet.Arcade.Sdk.dll')), - - # This list doesn't need to be updated anymore and can eventually be removed. - (Join-Path $basePath (Join-Path net9.0 'Microsoft.DotNet.ArcadeLogging.dll')), - (Join-Path $basePath (Join-Path net9.0 'Microsoft.DotNet.Arcade.Sdk.dll')), - (Join-Path $basePath (Join-Path net8.0 'Microsoft.DotNet.ArcadeLogging.dll')), - (Join-Path $basePath (Join-Path net8.0 'Microsoft.DotNet.Arcade.Sdk.dll')) - ) - $selectedPath = $null - foreach ($path in $possiblePaths) { - if (Test-Path $path -PathType Leaf) { - $selectedPath = $path - break - } - } + $selectedPath = Join-Path $basePath (Join-Path $buildTool.Framework 'Microsoft.DotNet.ArcadeLogging.dll') + if (-not $selectedPath) { - Write-PipelineTelemetryError -Category 'Build' -Message 'Unable to find arcade sdk logger assembly.' + Write-PipelineTelemetryError -Category 'Build' -Message "Unable to find arcade sdk logger assembly: $selectedPath" ExitWithExitCode 1 } + $args += "/logger:$selectedPath" } @@ -852,8 +849,8 @@ function MSBuild-Core() { } # When running on Azure Pipelines, override the returned exit code to avoid double logging. - # Skip this when the build is a child of the VMR orchestrator build. - if ($ci -and $env:SYSTEM_TEAMPROJECT -ne $null -and !$productBuild -and -not($properties -like "*DotNetBuildRepo=true*")) { + # Skip this when the build is a child of the VMR build. + if ($ci -and $env:SYSTEM_TEAMPROJECT -ne $null -and !$fromVMR) { Write-PipelineSetResult -Result "Failed" -Message "msbuild execution failed." # Exiting with an exit code causes the azure pipelines task to log yet another "noise" error # The above Write-PipelineSetResult will cause the task to be marked as failure without adding yet another error diff --git a/eng/common/tools.sh b/eng/common/tools.sh index d51f300c77..c1841c9dfd 100644 --- a/eng/common/tools.sh +++ b/eng/common/tools.sh @@ -5,6 +5,9 @@ # CI mode - set to true on CI server for PR validation build or official build. ci=${ci:-false} +# Build mode +source_build=${source_build:-false} + # Set to true to use the pipelines logger which will enable Azure logging output. # https://github.com/Microsoft/azure-pipelines-tasks/blob/master/docs/authoring/commands.md # This flag is meant as a temporary opt-opt for the feature while validate it across @@ -58,7 +61,8 @@ use_installed_dotnet_cli=${use_installed_dotnet_cli:-true} dotnetInstallScriptVersion=${dotnetInstallScriptVersion:-'v1'} # True to use global NuGet cache instead of restoring packages to repository-local directory. -if [[ "$ci" == true ]]; then +# Keep in sync with NuGetPackageroot in Arcade SDK's RepositoryLayout.props. +if [[ "$ci" == true || "$source_build" == true ]]; then use_global_nuget_cache=${use_global_nuget_cache:-false} else use_global_nuget_cache=${use_global_nuget_cache:-true} @@ -68,8 +72,8 @@ fi runtime_source_feed=${runtime_source_feed:-''} runtime_source_feed_key=${runtime_source_feed_key:-''} -# True if the build is a product build -product_build=${product_build:-false} +# True when the build is running within the VMR. +from_vmr=${from_vmr:-false} # Resolve any symlinks in the given path. function ResolvePath { @@ -296,8 +300,29 @@ function GetDotNetInstallScript { local root=$1 local install_script="$root/dotnet-install.sh" local install_script_url="https://builds.dotnet.microsoft.com/dotnet/scripts/$dotnetInstallScriptVersion/dotnet-install.sh" + local timestamp_file="$root/.dotnet-install.timestamp" + local should_download=false if [[ ! -a "$install_script" ]]; then + should_download=true + elif [[ -f "$timestamp_file" ]]; then + # Check if the script is older than 30 days using timestamp file + local download_time=$(cat "$timestamp_file" 2>/dev/null || echo "0") + local current_time=$(date +%s) + local age_seconds=$((current_time - download_time)) + + # 30 days = 30 * 24 * 60 * 60 = 2592000 seconds + if [[ $age_seconds -gt 2592000 ]]; then + echo "Existing install script is too old, re-downloading..." + should_download=true + fi + else + # No timestamp file exists, assume script is old and re-download + echo "No timestamp found for existing install script, re-downloading..." + should_download=true + fi + + if [[ "$should_download" == true ]]; then mkdir -p "$root" echo "Downloading '$install_script_url'" @@ -324,6 +349,9 @@ function GetDotNetInstallScript { ExitWithExitCode $exit_code } fi + + # Create timestamp file to track download time in seconds from epoch + date +%s > "$timestamp_file" fi # return value _GetDotNetInstallScript="$install_script" @@ -341,14 +369,12 @@ function InitializeBuildTool { _InitializeBuildToolCommand="msbuild" } -# Set RestoreNoHttpCache as a workaround for https://github.com/NuGet/Home/issues/3116 function GetNuGetPackageCachePath { if [[ -z ${NUGET_PACKAGES:-} ]]; then if [[ "$use_global_nuget_cache" == true ]]; then export NUGET_PACKAGES="$HOME/.nuget/packages/" else export NUGET_PACKAGES="$repo_root/.packages/" - export RESTORENOHTTPCACHE=true fi fi @@ -445,27 +471,13 @@ function MSBuild { fi local toolset_dir="${_InitializeToolset%/*}" - # new scripts need to work with old packages, so we need to look for the old names/versions - local selectedPath= - local possiblePaths=() - possiblePaths+=( "$toolset_dir/net/Microsoft.DotNet.ArcadeLogging.dll" ) - possiblePaths+=( "$toolset_dir/net/Microsoft.DotNet.Arcade.Sdk.dll" ) - - # This list doesn't need to be updated anymore and can eventually be removed. - possiblePaths+=( "$toolset_dir/net9.0/Microsoft.DotNet.ArcadeLogging.dll" ) - possiblePaths+=( "$toolset_dir/net9.0/Microsoft.DotNet.Arcade.Sdk.dll" ) - possiblePaths+=( "$toolset_dir/net8.0/Microsoft.DotNet.ArcadeLogging.dll" ) - possiblePaths+=( "$toolset_dir/net8.0/Microsoft.DotNet.Arcade.Sdk.dll" ) - for path in "${possiblePaths[@]}"; do - if [[ -f $path ]]; then - selectedPath=$path - break - fi - done + local selectedPath="$toolset_dir/net/Microsoft.DotNet.ArcadeLogging.dll" + if [[ -z "$selectedPath" ]]; then - Write-PipelineTelemetryError -category 'Build' "Unable to find arcade sdk logger assembly." + Write-PipelineTelemetryError -category 'Build' "Unable to find arcade sdk logger assembly: $selectedPath" ExitWithExitCode 1 fi + args+=( "-logger:$selectedPath" ) fi @@ -502,8 +514,8 @@ function MSBuild-Core { echo "Build failed with exit code $exit_code. Check errors above." # When running on Azure Pipelines, override the returned exit code to avoid double logging. - # Skip this when the build is a child of the VMR orchestrator build. - if [[ "$ci" == true && -n ${SYSTEM_TEAMPROJECT:-} && "$product_build" != true && "$properties" != *"DotNetBuildRepo=true"* ]]; then + # Skip this when the build is a child of the VMR build. + if [[ "$ci" == true && -n ${SYSTEM_TEAMPROJECT:-} && "$from_vmr" != true ]]; then Write-PipelineSetResult -result "Failed" -message "msbuild execution failed." # Exiting with an exit code causes the azure pipelines task to log yet another "noise" error # The above Write-PipelineSetResult will cause the task to be marked as failure without adding yet another error @@ -526,6 +538,7 @@ function GetDarc { fi "$eng_root/common/darc-init.sh" --toolpath "$darc_path" $version + darc_tool="$darc_path/darc" } # Returns a full path to an Arcade SDK task project file. diff --git a/eng/common/vmr-sync.ps1 b/eng/common/vmr-sync.ps1 new file mode 100644 index 0000000000..97302f3205 --- /dev/null +++ b/eng/common/vmr-sync.ps1 @@ -0,0 +1,138 @@ +<# +.SYNOPSIS + +This script is used for synchronizing the current repository into a local VMR. +It pulls the current repository's code into the specified VMR directory for local testing or +Source-Build validation. + +.DESCRIPTION + +The tooling used for synchronization will clone the VMR repository into a temporary folder if +it does not already exist. These clones can be reused in future synchronizations, so it is +recommended to dedicate a folder for this to speed up re-runs. + +.EXAMPLE + Synchronize current repository into a local VMR: + ./vmr-sync.ps1 -vmrDir "$HOME/repos/dotnet" -tmpDir "$HOME/repos/tmp" + +.PARAMETER tmpDir +Required. Path to the temporary folder where repositories will be cloned + +.PARAMETER vmrBranch +Optional. Branch of the 'dotnet/dotnet' repo to synchronize. The VMR will be checked out to this branch + +.PARAMETER azdevPat +Optional. Azure DevOps PAT to use for cloning private repositories. + +.PARAMETER vmrDir +Optional. Path to the dotnet/dotnet repository. When null, gets cloned to the temporary folder + +.PARAMETER debugOutput +Optional. Enables debug logging in the darc vmr command. + +.PARAMETER ci +Optional. Denotes that the script is running in a CI environment. +#> +param ( + [Parameter(Mandatory=$true, HelpMessage="Path to the temporary folder where repositories will be cloned")] + [string][Alias('t', 'tmp')]$tmpDir, + [string][Alias('b', 'branch')]$vmrBranch, + [string]$remote, + [string]$azdevPat, + [string][Alias('v', 'vmr')]$vmrDir, + [switch]$ci, + [switch]$debugOutput +) + +function Fail { + Write-Host "> $($args[0])" -ForegroundColor 'Red' +} + +function Highlight { + Write-Host "> $($args[0])" -ForegroundColor 'Cyan' +} + +$verbosity = 'verbose' +if ($debugOutput) { + $verbosity = 'debug' +} +# Validation + +if (-not $tmpDir) { + Fail "Missing -tmpDir argument. Please specify the path to the temporary folder where the repositories will be cloned" + exit 1 +} + +# Sanitize the input + +if (-not $vmrDir) { + $vmrDir = Join-Path $tmpDir 'dotnet' +} + +if (-not (Test-Path -Path $tmpDir -PathType Container)) { + New-Item -ItemType Directory -Path $tmpDir | Out-Null +} + +# Prepare the VMR + +if (-not (Test-Path -Path $vmrDir -PathType Container)) { + Highlight "Cloning 'dotnet/dotnet' into $vmrDir.." + git clone https://github.com/dotnet/dotnet $vmrDir + + if ($vmrBranch) { + git -C $vmrDir switch -c $vmrBranch + } +} +else { + if ((git -C $vmrDir diff --quiet) -eq $false) { + Fail "There are changes in the working tree of $vmrDir. Please commit or stash your changes" + exit 1 + } + + if ($vmrBranch) { + Highlight "Preparing $vmrDir" + git -C $vmrDir checkout $vmrBranch + git -C $vmrDir pull + } +} + +Set-StrictMode -Version Latest + +# Prepare darc + +Highlight 'Installing .NET, preparing the tooling..' +. .\eng\common\tools.ps1 +$dotnetRoot = InitializeDotNetCli -install:$true +$darc = Get-Darc +$dotnet = "$dotnetRoot\dotnet.exe" + +Highlight "Starting the synchronization of VMR.." + +# Synchronize the VMR +$darcArgs = ( + "vmr", "forwardflow", + "--tmp", $tmpDir, + "--$verbosity", + $vmrDir +) + +if ($ci) { + $darcArgs += ("--ci") +} + +if ($azdevPat) { + $darcArgs += ("--azdev-pat", $azdevPat) +} + +& "$darc" $darcArgs + +if ($LASTEXITCODE -eq 0) { + Highlight "Synchronization succeeded" +} +else { + Fail "Synchronization of repo to VMR failed!" + Fail "'$vmrDir' is left in its last state (re-run of this script will reset it)." + Fail "Please inspect the logs which contain path to the failing patch file (use -debugOutput to get all the details)." + Fail "Once you make changes to the conflicting VMR patch, commit it locally and re-run this script." + exit 1 +} diff --git a/eng/common/vmr-sync.sh b/eng/common/vmr-sync.sh new file mode 100644 index 0000000000..44239e331c --- /dev/null +++ b/eng/common/vmr-sync.sh @@ -0,0 +1,207 @@ +#!/bin/bash + +### This script is used for synchronizing the current repository into a local VMR. +### It pulls the current repository's code into the specified VMR directory for local testing or +### Source-Build validation. +### +### The tooling used for synchronization will clone the VMR repository into a temporary folder if +### it does not already exist. These clones can be reused in future synchronizations, so it is +### recommended to dedicate a folder for this to speed up re-runs. +### +### USAGE: +### Synchronize current repository into a local VMR: +### ./vmr-sync.sh --tmp "$HOME/repos/tmp" "$HOME/repos/dotnet" +### +### Options: +### -t, --tmp, --tmp-dir PATH +### Required. Path to the temporary folder where repositories will be cloned +### +### -b, --branch, --vmr-branch BRANCH_NAME +### Optional. Branch of the 'dotnet/dotnet' repo to synchronize. The VMR will be checked out to this branch +### +### --debug +### Optional. Turns on the most verbose logging for the VMR tooling +### +### --remote name:URI +### Optional. Additional remote to use during the synchronization +### This can be used to synchronize to a commit from a fork of the repository +### Example: 'runtime:https://github.com/yourfork/runtime' +### +### --azdev-pat +### Optional. Azure DevOps PAT to use for cloning private repositories. +### +### -v, --vmr, --vmr-dir PATH +### Optional. Path to the dotnet/dotnet repository. When null, gets cloned to the temporary folder + +source="${BASH_SOURCE[0]}" + +# resolve $source until the file is no longer a symlink +while [[ -h "$source" ]]; do + scriptroot="$( cd -P "$( dirname "$source" )" && pwd )" + source="$(readlink "$source")" + # if $source was a relative symlink, we need to resolve it relative to the path where the + # symlink file was located + [[ $source != /* ]] && source="$scriptroot/$source" +done +scriptroot="$( cd -P "$( dirname "$source" )" && pwd )" + +function print_help () { + sed -n '/^### /,/^$/p' "$source" | cut -b 5- +} + +COLOR_RED=$(tput setaf 1 2>/dev/null || true) +COLOR_CYAN=$(tput setaf 6 2>/dev/null || true) +COLOR_CLEAR=$(tput sgr0 2>/dev/null || true) +COLOR_RESET=uniquesearchablestring +FAILURE_PREFIX='> ' + +function fail () { + echo "${COLOR_RED}$FAILURE_PREFIX${1//${COLOR_RESET}/${COLOR_RED}}${COLOR_CLEAR}" >&2 +} + +function highlight () { + echo "${COLOR_CYAN}$FAILURE_PREFIX${1//${COLOR_RESET}/${COLOR_CYAN}}${COLOR_CLEAR}" +} + +tmp_dir='' +vmr_dir='' +vmr_branch='' +additional_remotes='' +verbosity=verbose +azdev_pat='' +ci=false + +while [[ $# -gt 0 ]]; do + opt="$(echo "$1" | tr "[:upper:]" "[:lower:]")" + case "$opt" in + -t|--tmp|--tmp-dir) + tmp_dir=$2 + shift + ;; + -v|--vmr|--vmr-dir) + vmr_dir=$2 + shift + ;; + -b|--branch|--vmr-branch) + vmr_branch=$2 + shift + ;; + --remote) + additional_remotes="$additional_remotes $2" + shift + ;; + --azdev-pat) + azdev_pat=$2 + shift + ;; + --ci) + ci=true + ;; + -d|--debug) + verbosity=debug + ;; + -h|--help) + print_help + exit 0 + ;; + *) + fail "Invalid argument: $1" + print_help + exit 1 + ;; + esac + + shift +done + +# Validation + +if [[ -z "$tmp_dir" ]]; then + fail "Missing --tmp-dir argument. Please specify the path to the temporary folder where the repositories will be cloned" + exit 1 +fi + +# Sanitize the input + +if [[ -z "$vmr_dir" ]]; then + vmr_dir="$tmp_dir/dotnet" +fi + +if [[ ! -d "$tmp_dir" ]]; then + mkdir -p "$tmp_dir" +fi + +if [[ "$verbosity" == "debug" ]]; then + set -x +fi + +# Prepare the VMR + +if [[ ! -d "$vmr_dir" ]]; then + highlight "Cloning 'dotnet/dotnet' into $vmr_dir.." + git clone https://github.com/dotnet/dotnet "$vmr_dir" + + if [[ -n "$vmr_branch" ]]; then + git -C "$vmr_dir" switch -c "$vmr_branch" + fi +else + if ! git -C "$vmr_dir" diff --quiet; then + fail "There are changes in the working tree of $vmr_dir. Please commit or stash your changes" + exit 1 + fi + + if [[ -n "$vmr_branch" ]]; then + highlight "Preparing $vmr_dir" + git -C "$vmr_dir" checkout "$vmr_branch" + git -C "$vmr_dir" pull + fi +fi + +set -e + +# Prepare darc + +highlight 'Installing .NET, preparing the tooling..' +source "./eng/common/tools.sh" +InitializeDotNetCli true +GetDarc +dotnetDir=$( cd ./.dotnet/; pwd -P ) +dotnet=$dotnetDir/dotnet + +highlight "Starting the synchronization of VMR.." +set +e + +if [[ -n "$additional_remotes" ]]; then + additional_remotes="--additional-remotes $additional_remotes" +fi + +if [[ -n "$azdev_pat" ]]; then + azdev_pat="--azdev-pat $azdev_pat" +fi + +ci_arg='' +if [[ "$ci" == "true" ]]; then + ci_arg="--ci" +fi + +# Synchronize the VMR + +export DOTNET_ROOT="$dotnetDir" + +"$darc_tool" vmr forwardflow \ + --tmp "$tmp_dir" \ + $azdev_pat \ + --$verbosity \ + $ci_arg \ + $additional_remotes \ + "$vmr_dir" + +if [[ $? == 0 ]]; then + highlight "Synchronization succeeded" +else + fail "Synchronization of repo to VMR failed!" + fail "'$vmr_dir' is left in its last state (re-run of this script will reset it)." + fail "Please inspect the logs which contain path to the failing patch file (use --debug to get all the details)." + fail "Once you make changes to the conflicting VMR patch, commit it locally and re-run this script." + exit 1 +fi diff --git a/eng/verify-nupkgs.ps1 b/eng/verify-nupkgs.ps1 index cd18465fe1..719c8aefd7 100644 --- a/eng/verify-nupkgs.ps1 +++ b/eng/verify-nupkgs.ps1 @@ -20,8 +20,8 @@ function Confirm-NugetPackages { Write-Verbose "Starting Confirm-NugetPackages." $expectedNumOfFiles = @{ "MSTest.Sdk" = 15 - "MSTest.TestFramework" = 154 - "MSTest.TestAdapter" = 93 + "MSTest.TestFramework" = 162 + "MSTest.TestAdapter" = 81 "MSTest" = 14 "MSTest.Analyzers" = 56 } diff --git a/es-metadata.yml b/es-metadata.yml new file mode 100644 index 0000000000..11af1e321e --- /dev/null +++ b/es-metadata.yml @@ -0,0 +1,8 @@ +schemaVersion: 0.0.1 +isProduction: true +accountableOwners: + service: 88877424-b87d-45a2-8ab8-48321d99a5d2 +routing: + defaultAreaPath: + org: devdiv + path: DevDiv\Testing Platforms diff --git a/global.json b/global.json index 1814242c89..d17d80f519 100644 --- a/global.json +++ b/global.json @@ -1,34 +1,42 @@ { "tools": { - "dotnet": "10.0.100-preview.4.25206.7", + "dotnet": "10.0.100-rc.2.25464.104", "runtimes": { "dotnet": [ "3.1.32", "6.0.36", "7.0.20", - "8.0.14", - "9.0.3" + "8.0.18", + "9.0.7" ], "dotnet/x86": [ "3.1.32", "6.0.36", - "9.0.3" + "9.0.7" ], "aspnetcore": [ - "9.0.3" + "9.0.7" ] }, "vs": { - "version": "17.8.0" + "version": "17.14.10" } }, "sdk": { - "version": "10.0.100-preview.4.25206.7", + "version": "10.0.100-rc.2.25464.104", + "paths": [ + ".dotnet", + "$host$" + ], + "errorMessage": "The .NET SDK could not be found, please run ./build.cmd on Windows or ./build.sh on Linux and macOS.", "allowPrerelease": true, "rollForward": "latestFeature" }, + "test": { + "runner": "Microsoft.Testing.Platform" + }, "msbuild-sdks": { - "Microsoft.DotNet.Arcade.Sdk": "10.0.0-beta.25229.4", + "Microsoft.DotNet.Arcade.Sdk": "11.0.0-beta.25479.7", "MSBuild.Sdk.Extras": "3.0.44" } } diff --git a/open-code.cmd b/open-code.cmd new file mode 100644 index 0000000000..82dda1d310 --- /dev/null +++ b/open-code.cmd @@ -0,0 +1,2 @@ +@echo off +powershell -ExecutionPolicy ByPass -NoProfile -command "& """%~dp0eng\build.ps1""" -vscode %*" \ No newline at end of file diff --git a/samples/.editorconfig b/samples/.editorconfig index 6fcce99672..2a4c6b6b21 100644 --- a/samples/.editorconfig +++ b/samples/.editorconfig @@ -9,3 +9,6 @@ root = false #### .NET Coding Conventions #### dotnet_analyzer_diagnostic.category-StyleCop.CSharp.DocumentationRules.severity = none # Disable StyleCop.CSharp.DocumentationRules + +# CA2007: Consider calling ConfigureAwait on the awaited task +dotnet_diagnostic.CA2007.severity = none diff --git a/samples/FxExtensibility/FxExtensibility.csproj b/samples/FxExtensibility/FxExtensibility.csproj index 13face57bd..e8a0b1e442 100644 --- a/samples/FxExtensibility/FxExtensibility.csproj +++ b/samples/FxExtensibility/FxExtensibility.csproj @@ -8,9 +8,11 @@ MSTest.Extensibility.Samples MSTest.Extensibility.Samples - TRACE + $(DefineConstants);TRACE prompt 4 + false + $(NoWarn);SA0001;EnableGenerateDocumentationFile diff --git a/samples/Playground/DebuggerUtility.cs b/samples/Playground/DebuggerUtility.cs index 1064e1a597..57f8bdf769 100644 --- a/samples/Playground/DebuggerUtility.cs +++ b/samples/Playground/DebuggerUtility.cs @@ -28,7 +28,7 @@ private static bool AttachVSToProcess(int? pid, int? vsPid, bool enableLog = fal using var process = Process.GetProcessById(pid.Value); Trace($"Starting with pid '{pid}({process.ProcessName})', and vsPid '{vsPid}'", enabled: enableLog); Trace($"Using pid: {pid} to get parent VS.", enabled: enableLog); - Process? vs = GetVsFromPid(Process.GetProcessById(vsPid ?? process.Id)); + using Process? vs = GetVsFromPid(Process.GetProcessById(vsPid ?? process.Id)); if (vs != null) { @@ -38,7 +38,7 @@ private static bool AttachVSToProcess(int? pid, int? vsPid, bool enableLog = fal } Trace("Parent VS not found, finding the first VS that started.", enabled: enableLog); - Process? firstVsProcess = GetFirstVsProcess(); + using Process? firstVsProcess = GetFirstVsProcess(); if (firstVsProcess != null) { @@ -149,7 +149,6 @@ private static bool AttachVs(Process vs, int pid, bool enableLog = false) if (dn.StartsWith("!VisualStudio.DTE.", StringComparison.Ordinal) && dn.EndsWith(dteSuffix, StringComparison.Ordinal)) { - object dbg, lps; runningObjectTable.GetObject(moniker[0], out object dte); // The COM object can be busy, we retry few times, hoping that it won't be busy next time. @@ -157,8 +156,8 @@ private static bool AttachVs(Process vs, int pid, bool enableLog = false) { try { - dbg = dte.GetType().InvokeMember("Debugger", BindingFlags.GetProperty, null, dte, null, CultureInfo.InvariantCulture)!; - lps = dbg.GetType().InvokeMember("LocalProcesses", BindingFlags.GetProperty, null, dbg, null, CultureInfo.InvariantCulture)!; + object dbg = dte.GetType().InvokeMember("Debugger", BindingFlags.GetProperty, null, dte, null, CultureInfo.InvariantCulture)!; + object lps = dbg.GetType().InvokeMember("LocalProcesses", BindingFlags.GetProperty, null, dbg, null, CultureInfo.InvariantCulture)!; var lpn = (System.Collections.IEnumerator)lps.GetType().InvokeMember("GetEnumerator", BindingFlags.InvokeMethod, null, lps, null, CultureInfo.InvariantCulture)!; while (lpn.MoveNext()) diff --git a/samples/Playground/Playground.csproj b/samples/Playground/Playground.csproj index b8570b8348..ba8afed52d 100644 --- a/samples/Playground/Playground.csproj +++ b/samples/Playground/Playground.csproj @@ -5,15 +5,20 @@ net9.0 enable false - $(NoWarn);NETSDK1023 + $(NoWarn);NETSDK1023;SA0001;EnableGenerateDocumentationFile true + false + + + + diff --git a/samples/Playground/Program.cs b/samples/Playground/Program.cs index 4a3d04350a..8e12c8794c 100644 --- a/samples/Playground/Program.cs +++ b/samples/Playground/Program.cs @@ -26,7 +26,7 @@ public static async Task Main(string[] args) { #if NETCOREAPP // To attach to the children - Microsoft.Testing.TestInfrastructure.DebuggerUtility.AttachCurrentProcessToParentVSProcess(); + // Microsoft.Testing.TestInfrastructure.DebuggerUtility.AttachCurrentProcessToParentVSProcess(); #endif ITestApplicationBuilder testApplicationBuilder = await TestApplication.CreateBuilderAsync(args); @@ -57,7 +57,7 @@ public static async Task Main(string[] args) using TestingPlatformClient client = await TestingPlatformClientFactory.StartAsServerAndConnectToTheClientAsync(Environment.ProcessPath!); await client.InitializeAsync(); - List testNodeUpdates = new(); + List testNodeUpdates = []; ResponseListener discoveryResponse = await client.DiscoverTestsAsync(Guid.NewGuid(), node => { testNodeUpdates.AddRange(node); @@ -65,7 +65,7 @@ public static async Task Main(string[] args) }); await discoveryResponse.WaitCompletionAsync(); - ResponseListener runRequest = await client.RunTestsAsync(Guid.NewGuid(), testNodeUpdates.Select(x => x.Node).ToArray(), _ => Task.CompletedTask); + ResponseListener runRequest = await client.RunTestsAsync(Guid.NewGuid(), [.. testNodeUpdates.Select(x => x.Node)], _ => Task.CompletedTask); await runRequest.WaitCompletionAsync(); await client.ExitAsync(); @@ -76,7 +76,7 @@ public static async Task Main(string[] args) } } -internal sealed class DummyAdapter() : ITestFramework, IDataProducer +internal sealed class DummyAdapter : ITestFramework, IDataProducer { public string Uid => nameof(DummyAdapter); @@ -86,7 +86,7 @@ internal sealed class DummyAdapter() : ITestFramework, IDataProducer public string Description => string.Empty; - public Type[] DataTypesProduced => new[] { typeof(TestNodeUpdateMessage) }; + public Type[] DataTypesProduced => [typeof(TestNodeUpdateMessage)]; public Task CloseTestSessionAsync(CloseTestSessionContext context) => Task.FromResult(new CloseTestSessionResult { IsSuccess = true }); diff --git a/samples/Playground/ServerMode/TestNodeUpdateCollector.cs b/samples/Playground/ServerMode/TestNodeUpdateCollector.cs index 067a4f4410..799aab6ab5 100644 --- a/samples/Playground/ServerMode/TestNodeUpdateCollector.cs +++ b/samples/Playground/ServerMode/TestNodeUpdateCollector.cs @@ -10,7 +10,7 @@ public class TestNodeUpdateCollector private readonly TaskCompletionSource _taskCompletionSource = new(); private readonly Func? _completeCollector; - public ConcurrentBag TestNodeUpdates { get; } = new(); + public ConcurrentBag TestNodeUpdates { get; } = []; public TestNodeUpdateCollector(Func? completeCollectorWhen = null) => _completeCollector = completeCollectorWhen; diff --git a/samples/Playground/ServerMode/TestingPlatformClientFactory.cs b/samples/Playground/ServerMode/TestingPlatformClientFactory.cs index 44491c2798..5fe8dc28d6 100644 --- a/samples/Playground/ServerMode/TestingPlatformClientFactory.cs +++ b/samples/Playground/ServerMode/TestingPlatformClientFactory.cs @@ -5,6 +5,7 @@ using System.Net.Sockets; using Microsoft.Testing.Platform.ServerMode.IntegrationTests.Messages.V100; +using Microsoft.Testing.TestInfrastructure; namespace MSTest.Acceptance.IntegrationTests.Messages.V100; @@ -382,25 +383,3 @@ public static class WellKnownEnvironmentVariables "TESTINGPLATFORM_CONSOLEOUTPUTDEVICE_SKIP_BANNER" ]; } - -public static class RootFinder -{ - public static string Find() - { - string path = AppContext.BaseDirectory; - string dir = path; - while (Directory.GetDirectoryRoot(dir) != dir) - { - if (Directory.Exists(Path.Combine(dir, ".git"))) - { - return dir; - } - else - { - dir = Directory.GetParent(dir)!.ToString(); - } - } - - throw new InvalidOperationException($"Could not find solution root, .git not found in {path} or any parent directory."); - } -} diff --git a/samples/Playground/ServerMode/v1.0.0/TestingPlatformClient.cs b/samples/Playground/ServerMode/v1.0.0/TestingPlatformClient.cs index 3b40c4d341..d789e2bd04 100644 --- a/samples/Playground/ServerMode/v1.0.0/TestingPlatformClient.cs +++ b/samples/Playground/ServerMode/v1.0.0/TestingPlatformClient.cs @@ -64,14 +64,9 @@ private async Task CheckedInvokeAsync(Func func) { await func(); } - catch (Exception ex) + catch (Exception ex) when (_disconnectionReason.Length > 0) { - if (_disconnectionReason.Length > 0) - { - throw new InvalidOperationException($"{ex.Message}\n{_disconnectionReason}", ex); - } - - throw; + throw new InvalidOperationException($"{ex.Message}\n{_disconnectionReason}", ex); } } @@ -171,11 +166,9 @@ private sealed class TargetHandler private readonly ConcurrentDictionary _listeners = new(); - private readonly ConcurrentBag _logListeners - = new(); + private readonly ConcurrentBag _logListeners = []; - private readonly ConcurrentBag _telemetryPayloads - = new(); + private readonly ConcurrentBag _telemetryPayloads = []; public void RegisterTelemetryListener(TelemetryCollector listener) => _telemetryPayloads.Add(listener); diff --git a/samples/Playground/Tests.cs b/samples/Playground/Tests.cs index c3b540e601..b77a79c78b 100644 --- a/samples/Playground/Tests.cs +++ b/samples/Playground/Tests.cs @@ -13,17 +13,7 @@ namespace Playground; public class TestClass { [TestMethod] - [DynamicData(nameof(Data))] - public void Test3(int a, int b) + public void Test1() { } - - public static IEnumerable<(int A, int B)> Data - { - get - { - yield return (1, 2); - yield return (3, 4); - } - } } diff --git a/samples/public/Directory.Build.props b/samples/public/Directory.Build.props index 0cee8689b5..20019790aa 100644 --- a/samples/public/Directory.Build.props +++ b/samples/public/Directory.Build.props @@ -1,13 +1,15 @@ - 9.0.0 - 17.13.1 - 3.8.3 - 1.0.0-alpha.25167.5 - 1.50.0 - 1.6.3 + 9.4.0 + 17.14.2 + 3.10.1 + 1.0.0-alpha.25405.1 + 1.54.0 + 1.8.1 17.13.0 + false + $(NoWarn);SA0001;EnableGenerateDocumentationFile diff --git a/samples/public/global.json b/samples/public/global.json index 11074d3c4b..17e37be137 100644 --- a/samples/public/global.json +++ b/samples/public/global.json @@ -1,5 +1,5 @@ { "msbuild-sdks": { - "MSTest.Sdk": "3.8.1" + "MSTest.Sdk": "3.10.1" } } diff --git a/src/Adapter/MSTest.Engine/BannedSymbols.txt b/src/Adapter/MSTest.Engine/BannedSymbols.txt index 8c5d85ec6d..f7256d3c54 100644 --- a/src/Adapter/MSTest.Engine/BannedSymbols.txt +++ b/src/Adapter/MSTest.Engine/BannedSymbols.txt @@ -2,5 +2,5 @@ M:System.Threading.Tasks.Task.Run(System.Func{System.Threading.Tasks.Task},System.Threading.CancellationToken); 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); Use 'RoslynDebug.Assert' instead M:System.Diagnostics.Debug.Assert(System.Boolean,System.String); Use 'RoslynDebug.Assert' instead diff --git a/src/Adapter/MSTest.Engine/RepositoryVersion.cs.template b/src/Adapter/MSTest.Engine/BuildInfo.cs.template similarity index 100% rename from src/Adapter/MSTest.Engine/RepositoryVersion.cs.template rename to src/Adapter/MSTest.Engine/BuildInfo.cs.template diff --git a/src/Adapter/MSTest.Engine/Engine/BFSTestNodeVisitor.cs b/src/Adapter/MSTest.Engine/Engine/BFSTestNodeVisitor.cs index 9d3cf88a38..faa7301a1b 100644 --- a/src/Adapter/MSTest.Engine/Engine/BFSTestNodeVisitor.cs +++ b/src/Adapter/MSTest.Engine/Engine/BFSTestNodeVisitor.cs @@ -9,7 +9,6 @@ namespace Microsoft.Testing.Framework; -#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 BFSTestNodeVisitor { private readonly IEnumerable _rootTestNodes; @@ -29,12 +28,12 @@ public BFSTestNodeVisitor(IEnumerable rootTestNodes, ITestExecutionFil _testArgumentsManager = testArgumentsManager; } - internal KeyValuePair>[] DuplicatedNodes { get; private set; } = Array.Empty>>(); + internal KeyValuePair>[] DuplicatedNodes { get; private set; } = []; public async Task VisitAsync(Func onIncludedTestNodeAsync) { // This is case sensitive, and culture insensitive, to keep UIDs unique, and comparable between different system. - Dictionary> testNodesByUid = new(); + Dictionary> testNodesByUid = []; Queue<(TestNode CurrentNode, TestNodeUid? ParentNodeUid, StringBuilder NodeFullPath)> queue = new(); foreach (TestNode node in _rootTestNodes) { @@ -47,7 +46,7 @@ public async Task VisitAsync(Func onIncludedTestNo if (!testNodesByUid.TryGetValue(currentNode.StableUid, out List? testNodes)) { - testNodes = new(); + testNodes = []; testNodesByUid.Add(currentNode.StableUid, testNodes); } @@ -78,14 +77,14 @@ public async Task VisitAsync(Func onIncludedTestNo // If the node is expandable, we expand it (replacing the original node) if (TestArgumentsManager.IsExpandableTestNode(currentNode)) { - currentNode = await _testArgumentsManager.ExpandTestNodeAsync(currentNode); + currentNode = await _testArgumentsManager.ExpandTestNodeAsync(currentNode).ConfigureAwait(false); } // If the node is not filtered out by the test execution filter, we call the callback with the node. if (_testExecutionFilter is not TestNodeUidListFilter listFilter || listFilter.TestNodeUids.Any(uid => currentNode.StableUid.ToPlatformTestNodeUid() == uid)) { - await onIncludedTestNodeAsync(currentNode, parentNodeUid); + await onIncludedTestNodeAsync(currentNode, parentNodeUid).ConfigureAwait(false); } foreach (TestNode childNode in currentNode.Tests) @@ -94,7 +93,7 @@ public async Task VisitAsync(Func onIncludedTestNo } } - DuplicatedNodes = testNodesByUid.Where(x => x.Value.Count > 1).ToArray(); + DuplicatedNodes = [.. testNodesByUid.Where(x => x.Value.Count > 1)]; } private static PropertyBag CreatePropertyBagForFilter(IProperty[] properties) diff --git a/src/Adapter/MSTest.Engine/Engine/TestArgumentsManager.cs b/src/Adapter/MSTest.Engine/Engine/TestArgumentsManager.cs index 1003fe27ef..1fb6ec6235 100644 --- a/src/Adapter/MSTest.Engine/Engine/TestArgumentsManager.cs +++ b/src/Adapter/MSTest.Engine/Engine/TestArgumentsManager.cs @@ -3,11 +3,13 @@ using Microsoft.Testing.Platform; +using Polyfills; + namespace Microsoft.Testing.Framework; internal sealed class TestArgumentsManager : ITestArgumentsManager { - private readonly Dictionary> _testArgumentsEntryProviders = new(); + private readonly Dictionary> _testArgumentsEntryProviders = []; private bool _isRegistrationFrozen; public void RegisterTestArgumentsEntryProvider( @@ -19,12 +21,10 @@ public void RegisterTestArgumentsEntryProvider( throw new InvalidOperationException("Cannot register TestArgumentsEntry provider after registration is frozen."); } - if (_testArgumentsEntryProviders.ContainsKey(testNodeStableUid)) + if (!_testArgumentsEntryProviders.TryAdd(testNodeStableUid, argumentPropertiesProviderCallback)) { throw new InvalidOperationException($"TestArgumentsEntry provider is already registered for test node with UID '{testNodeStableUid}'."); } - - _testArgumentsEntryProviders.Add(testNodeStableUid, argumentPropertiesProviderCallback); } internal void FreezeRegistration() => _isRegistrationFrozen = true; @@ -37,8 +37,6 @@ internal async Task ExpandTestNodeAsync(TestNode currentNode) { RoslynDebug.Assert(IsExpandableTestNode(currentNode), "Test node is not expandable"); - var expandableTestNode = (IExpandableTestNode)currentNode; - int argumentsRowIndex = -1; bool isIndexArgumentPropertiesProvider = false; if (!_testArgumentsEntryProviders.TryGetValue( @@ -53,8 +51,8 @@ internal async Task ExpandTestNodeAsync(TestNode currentNode) }; } - HashSet expandedTestNodeUids = new(); - List expandedTestNodes = new(currentNode.Tests); + HashSet expandedTestNodeUids = []; + List expandedTestNodes = [.. currentNode.Tests]; switch (currentNode) { case IParameterizedTestNode parameterizedTestNode: @@ -67,7 +65,7 @@ internal async Task ExpandTestNodeAsync(TestNode currentNode) break; case ITaskParameterizedTestNode parameterizedTestNode: - foreach (object? arguments in await parameterizedTestNode.GetArguments()) + foreach (object? arguments in await parameterizedTestNode.GetArguments().ConfigureAwait(false)) { ExpandNodeWithArguments(currentNode, arguments, ref argumentsRowIndex, expandedTestNodes, expandedTestNodeUids, argumentPropertiesProvider, isIndexArgumentPropertiesProvider); @@ -77,7 +75,7 @@ internal async Task ExpandTestNodeAsync(TestNode currentNode) #if NET case IAsyncParameterizedTestNode parameterizedTestNode: - await foreach (object? arguments in parameterizedTestNode.GetArguments()) + await foreach (object? arguments in parameterizedTestNode.GetArguments().ConfigureAwait(false)) { ExpandNodeWithArguments(currentNode, arguments, ref argumentsRowIndex, expandedTestNodes, expandedTestNodeUids, argumentPropertiesProvider, isIndexArgumentPropertiesProvider); @@ -98,7 +96,7 @@ internal async Task ExpandTestNodeAsync(TestNode currentNode) DisplayName = currentNode.DisplayName, OverriddenEdgeName = currentNode.OverriddenEdgeName, Properties = currentNode.Properties, - Tests = expandedTestNodes.ToArray(), + Tests = [.. expandedTestNodes], }; return expandedNode; diff --git a/src/Adapter/MSTest.Engine/Engine/TestFixtureManager.cs b/src/Adapter/MSTest.Engine/Engine/TestFixtureManager.cs index 4c26895bc4..9da126c44b 100644 --- a/src/Adapter/MSTest.Engine/Engine/TestFixtureManager.cs +++ b/src/Adapter/MSTest.Engine/Engine/TestFixtureManager.cs @@ -10,13 +10,13 @@ namespace Microsoft.Testing.Framework; internal sealed class TestFixtureManager : ITestFixtureManager { - private readonly Dictionary>> _fixtureInstancesByFixtureId = new(); - private readonly Dictionary _fixtureIdsUsedByTestNode = new(); + private readonly Dictionary>> _fixtureInstancesByFixtureId = []; + private readonly Dictionary _fixtureIdsUsedByTestNode = []; // We could improve this by doing some optimistic lock but we expect a rather low contention on this. // We use a dictionary as performance improvement because we know that when the registration is complete // we will only read the collection (so no need for concurrency handling). - private readonly Dictionary _fixtureUses = new(); + private readonly Dictionary _fixtureUses = []; private readonly CancellationToken _cancellationToken; private bool _isRegistrationFrozen; private bool _isUsageRegistrationFrozen; @@ -58,7 +58,7 @@ public async Task GetFixtureAsync(string fixtureId) if (!fixture.IsValueCreated || !fixture.Value.IsCompleted) { - await fixture.Value; + await fixture.Value.ConfigureAwait(false); } // We can safely cast here because we know that the fixture is of type TFixture and is awaited. @@ -79,7 +79,7 @@ internal void RegisterFixtureUsage(TestNode testNode, string[] fixtureIds) return; } - _fixtureIdsUsedByTestNode.Add(testNode, fixtureIds.Select(x => new FixtureId(x)).ToArray()); + _fixtureIdsUsedByTestNode.Add(testNode, [.. fixtureIds.Select(x => new FixtureId(x))]); foreach (string fixtureId in fixtureIds) { if (!_fixtureUses.TryGetValue(fixtureId, out CountHolder? uses)) @@ -114,7 +114,7 @@ internal async Task SetupUsedFixturesAsync(TestNode testNode) { if (!lazyFixture.IsValueCreated || !lazyFixture.Value.IsCompleted) { - await lazyFixture.Value; + await lazyFixture.Value.ConfigureAwait(false); } } } @@ -142,7 +142,7 @@ internal async Task CleanUnusedFixturesAsync(TestNode testNode) // cleaning the fixture multiple times. if (usesCount == 0) { - await CleanupAndDisposeFixtureAsync(fixtureId); + await CleanupAndDisposeFixtureAsync(fixtureId).ConfigureAwait(false); } } } @@ -167,7 +167,7 @@ private void TryRegisterFixture(string fixtureId, Func> { fixtureInstancesPerType.Add( typeof(TFixture), - new(async () => await CreateAndInitializeFixtureAsync(asyncFactory, _cancellationToken), LazyThreadSafetyMode.ExecutionAndPublication)); + new(async () => await CreateAndInitializeFixtureAsync(asyncFactory, _cancellationToken).ConfigureAwait(false), LazyThreadSafetyMode.ExecutionAndPublication)); } } else @@ -177,14 +177,14 @@ private void TryRegisterFixture(string fixtureId, Func> new() { [typeof(TFixture)] = new( - async () => await CreateAndInitializeFixtureAsync(asyncFactory, _cancellationToken), + async () => await CreateAndInitializeFixtureAsync(asyncFactory, _cancellationToken).ConfigureAwait(false), LazyThreadSafetyMode.ExecutionAndPublication), }); } static async Task CreateAndInitializeFixtureAsync(Func> asyncFactory, CancellationToken cancellationToken) { - TFixture fixture = await asyncFactory(); + TFixture fixture = await asyncFactory().ConfigureAwait(false); return fixture; } } @@ -207,7 +207,7 @@ private async Task CleanupAndDisposeFixtureAsync(FixtureId fixtureId) object fixture = lazyFixture.Value.Result; #pragma warning restore VSTHRD103 // Call async methods when in an async method - await DisposeHelper.DisposeAsync(fixture); + await DisposeHelper.DisposeAsync(fixture).ConfigureAwait(false); } } diff --git a/src/Adapter/MSTest.Engine/Engine/TestFrameworkEngine.cs b/src/Adapter/MSTest.Engine/Engine/TestFrameworkEngine.cs index f78f930f7a..00527b1d27 100644 --- a/src/Adapter/MSTest.Engine/Engine/TestFrameworkEngine.cs +++ b/src/Adapter/MSTest.Engine/Engine/TestFrameworkEngine.cs @@ -38,8 +38,7 @@ public TestFrameworkEngine(TestFrameworkConfiguration testFrameworkConfiguration _configuration = new(configuration); } - public Type[] DataTypesProduced { get; } - = new Type[1] { typeof(TestNodeUpdateMessage) }; + public Type[] DataTypesProduced { get; } = [typeof(TestNodeUpdateMessage)]; public string Uid => _extension.Uid; @@ -49,20 +48,20 @@ public TestFrameworkEngine(TestFrameworkConfiguration testFrameworkConfiguration public string Description => _extension.Description; - public async Task IsEnabledAsync() => await _extension.IsEnabledAsync(); + public async Task IsEnabledAsync() => await _extension.IsEnabledAsync().ConfigureAwait(false); public async Task ExecuteRequestAsync(TestExecutionRequest testExecutionRequest, IMessageBus messageBus, CancellationToken cancellationToken) => testExecutionRequest switch { - DiscoverTestExecutionRequest discoveryRequest => await ExecuteTestNodeDiscoveryAsync(discoveryRequest, messageBus, cancellationToken), - RunTestExecutionRequest runRequest => await ExecuteTestNodeRunAsync(runRequest, messageBus, cancellationToken), + DiscoverTestExecutionRequest discoveryRequest => await ExecuteTestNodeDiscoveryAsync(discoveryRequest, messageBus, cancellationToken).ConfigureAwait(false), + RunTestExecutionRequest runRequest => await ExecuteTestNodeRunAsync(runRequest, messageBus, cancellationToken).ConfigureAwait(false), _ => Result.Fail($"Unexpected request type: '{testExecutionRequest.GetType().FullName}'"), }; private async Task ExecuteTestNodeRunAsync(RunTestExecutionRequest request, IMessageBus messageBus, CancellationToken cancellationToken) { - List allRootTestNodes = new(); + List allRootTestNodes = []; TestFixtureManager fixtureManager = new(cancellationToken); TestArgumentsManager argumentsManager = new(); TestSessionContext testSessionContext = new(_configuration, fixtureManager, argumentsManager, request.Session.SessionUid, @@ -72,7 +71,7 @@ private async Task ExecuteTestNodeRunAsync(RunTestExecutionRequest reque { foreach (ITestNodesBuilder testNodeBuilder in _testNodesBuilders) { - TestNode[] testNodes = await testNodeBuilder.BuildAsync(testSessionContext); + TestNode[] testNodes = await testNodeBuilder.BuildAsync(testSessionContext).ConfigureAwait(false); allRootTestNodes.AddRange(testNodes); } @@ -96,11 +95,11 @@ await testNodesVisitor.VisitAsync((testNode, parentTestNodeUid) => .OfType() .SingleOrDefault() .UsedFixtureIds - ?? Array.Empty(); + ?? []; fixtureManager.RegisterFixtureUsage(testNode, fixtureIds); return Task.CompletedTask; - }); + }).ConfigureAwait(false); if (testNodesVisitor.DuplicatedNodes.Length > 0) { @@ -120,13 +119,13 @@ await testNodesVisitor.VisitAsync((testNode, parentTestNodeUid) => testNodeRunner.StartTests(); // Finally, we want to wait for all tests to complete. - return await testNodeRunner.WaitAllTestsAsync(cancellationToken); + return await testNodeRunner.WaitAllTestsAsync(cancellationToken).ConfigureAwait(false); } finally { foreach (ITestNodesBuilder testNodeBuilder in _testNodesBuilders) { - await DisposeHelper.DisposeAsync(testNodeBuilder); + await DisposeHelper.DisposeAsync(testNodeBuilder).ConfigureAwait(false); } } @@ -142,7 +141,7 @@ Task PublishDataAsync(IData data) private async Task ExecuteTestNodeDiscoveryAsync(DiscoverTestExecutionRequest request, IMessageBus messageBus, CancellationToken cancellationToken) { - List allRootTestNodes = new(); + List allRootTestNodes = []; TestFixtureManager fixtureManager = new(cancellationToken); TestArgumentsManager argumentsManager = new(); TestSessionContext testSessionContext = new(_configuration, fixtureManager, argumentsManager, request.Session.SessionUid, @@ -152,7 +151,7 @@ private async Task ExecuteTestNodeDiscoveryAsync(DiscoverTestExecutionRe { foreach (ITestNodesBuilder testNodeBuilder in _testNodesBuilders) { - TestNode[] testNodes = await testNodeBuilder.BuildAsync(testSessionContext); + TestNode[] testNodes = await testNodeBuilder.BuildAsync(testSessionContext).ConfigureAwait(false); allRootTestNodes.AddRange(testNodes); } @@ -172,8 +171,8 @@ await testNodesVisitor.VisitAsync(async (testNode, parentTestNodeUid) => } await messageBus.PublishAsync(this, new TestNodeUpdateMessage(request.Session.SessionUid, progressNode, - parentTestNodeUid?.ToPlatformTestNodeUid())); - }); + parentTestNodeUid?.ToPlatformTestNodeUid())).ConfigureAwait(false); + }).ConfigureAwait(false); if (testNodesVisitor.DuplicatedNodes.Length > 0) { @@ -192,7 +191,7 @@ await testNodesVisitor.VisitAsync(async (testNode, parentTestNodeUid) => { foreach (ITestNodesBuilder testNodeBuilder in _testNodesBuilders) { - await DisposeHelper.DisposeAsync(testNodeBuilder); + await DisposeHelper.DisposeAsync(testNodeBuilder).ConfigureAwait(false); } } diff --git a/src/Adapter/MSTest.Engine/Engine/TestSessionContext.cs b/src/Adapter/MSTest.Engine/Engine/TestSessionContext.cs index 8db024032c..22042a1e5b 100644 --- a/src/Adapter/MSTest.Engine/Engine/TestSessionContext.cs +++ b/src/Adapter/MSTest.Engine/Engine/TestSessionContext.cs @@ -27,5 +27,5 @@ public TestSessionContext(IConfiguration configuration, ITestFixtureManager _, I public IConfiguration Configuration { get; } public async Task AddTestAttachmentAsync(FileInfo file, string displayName, string? description = null) - => await _publishDataAsync(new SessionFileArtifact(_sessionUid, file, displayName, description)); + => await _publishDataAsync(new SessionFileArtifact(_sessionUid, file, displayName, description)).ConfigureAwait(false); } diff --git a/src/Adapter/MSTest.Engine/Engine/ThreadPoolTestNodeRunner.cs b/src/Adapter/MSTest.Engine/Engine/ThreadPoolTestNodeRunner.cs index 3ef4aa070f..3070130114 100644 --- a/src/Adapter/MSTest.Engine/Engine/ThreadPoolTestNodeRunner.cs +++ b/src/Adapter/MSTest.Engine/Engine/ThreadPoolTestNodeRunner.cs @@ -16,7 +16,7 @@ namespace Microsoft.Testing.Framework; internal sealed class ThreadPoolTestNodeRunner : IDisposable { private readonly SemaphoreSlim? _maxParallelTests; - private readonly ConcurrentBag> _runningTests = new(); + private readonly ConcurrentBag> _runningTests = []; private readonly ConcurrentDictionary _runningTestNodeUids = new(); private readonly CountdownEvent _ensureTaskQueuedCountdownEvent = new(1); private readonly Func _publishDataAsync; @@ -63,12 +63,12 @@ public void EnqueueTest(TestNode frameworkTestNode, TestNodeUid? parentTestNodeU { // We don't have a timeout here because we can have really slow fixture and it's on user // the decision on how much to wait for it. - await _waitForStart.Task; + await _waitForStart.Task.ConfigureAwait(false); // Handle the global parallelism. if (_maxParallelTests is not null) { - await _maxParallelTests.WaitAsync(); + await _maxParallelTests.WaitAsync().ConfigureAwait(false); } try @@ -77,9 +77,9 @@ public void EnqueueTest(TestNode frameworkTestNode, TestNodeUid? parentTestNodeU PlatformTestNode progressNode = frameworkTestNode.ToPlatformTestNode(); progressNode.Properties.Add(InProgressTestNodeStateProperty.CachedInstance); - await _publishDataAsync(new TestNodeUpdateMessage(_sessionUid, progressNode, parentTestNodeUid?.ToPlatformTestNodeUid())); + await _publishDataAsync(new TestNodeUpdateMessage(_sessionUid, progressNode, parentTestNodeUid?.ToPlatformTestNodeUid())).ConfigureAwait(false); - Result result = await CreateTestRunTaskAsync(frameworkTestNode, parentTestNodeUid); + Result result = await CreateTestRunTaskAsync(frameworkTestNode, parentTestNodeUid).ConfigureAwait(false); _runningTestNodeUids.TryRemove(frameworkTestNode.StableUid, out int count); @@ -118,7 +118,7 @@ private async Task CreateTestRunTaskAsync(TestNode testNode, TestNodeUid { try { - await _testFixtureManager.SetupUsedFixturesAsync(testNode); + await _testFixtureManager.SetupUsedFixturesAsync(testNode).ConfigureAwait(false); } catch (Exception ex) { @@ -135,7 +135,7 @@ private async Task CreateTestRunTaskAsync(TestNode testNode, TestNodeUid switch (testNode) { case IAsyncActionTestNode actionTestNode: - await actionTestNode.InvokeAsync(testExecutionContext); + await actionTestNode.InvokeAsync(testExecutionContext).ConfigureAwait(false); break; case IActionTestNode actionTestNode: @@ -145,7 +145,7 @@ private async Task CreateTestRunTaskAsync(TestNode testNode, TestNodeUid case IParameterizedAsyncActionTestNode actionTestNode: await actionTestNode.InvokeAsync( testExecutionContext, - action => InvokeTestNodeAndPublishResultAsync(testNode, parentTestNodeUid, (_, _) => action(), skipPublishResult: false)); + action => InvokeTestNodeAndPublishResultAsync(testNode, parentTestNodeUid, (_, _) => action(), skipPublishResult: false)).ConfigureAwait(false); break; default: @@ -154,12 +154,12 @@ await actionTestNode.InvokeAsync( }, // Because parameterized tests report multiple results (one per parameter set), we don't want to publish the result // of the overall test node execution, but only the results of the individual parameterized tests. - skipPublishResult: testNode is IParameterizedAsyncActionTestNode); + skipPublishResult: testNode is IParameterizedAsyncActionTestNode).ConfigureAwait(false); // Try to cleanup the fixture is not more used. try { - await _testFixtureManager.CleanUnusedFixturesAsync(testNode); + await _testFixtureManager.CleanUnusedFixturesAsync(testNode).ConfigureAwait(false); return result; } catch (Exception ex) @@ -177,8 +177,8 @@ public async Task WaitAllTestsAsync(CancellationToken cancellationToken) try { _ensureTaskQueuedCountdownEvent.Signal(); - await _ensureTaskQueuedCountdownEvent.WaitAsync(cancellationToken); - Result[] results = await Task.WhenAll(_runningTests); + await _ensureTaskQueuedCountdownEvent.WaitAsync(cancellationToken).ConfigureAwait(false); + Result[] results = await Task.WhenAll(_runningTests).ConfigureAwait(false); return Result.Combine(results); } catch (OperationCanceledException ex) when (ex.CancellationToken == cancellationToken) @@ -216,7 +216,7 @@ private async Task InvokeTestNodeAndPublishResultAsync(TestNode testNode // If we're already enqueued we cancel the test before the start // The test could not use the cancellation and we should wait the end of the test self to cancel. _cancellationToken.ThrowIfCancellationRequested(); - await testNodeInvokeAction(testNode, testExecutionContext); + await testNodeInvokeAction(testNode, testExecutionContext).ConfigureAwait(false); if (!platformTestNode.Properties.Any()) { @@ -245,7 +245,7 @@ private async Task InvokeTestNodeAndPublishResultAsync(TestNode testNode if (!skipPublishResult) { - await _publishDataAsync(new TestNodeUpdateMessage(_sessionUid, platformTestNode, parentTestNodeUid?.ToPlatformTestNodeUid())); + await _publishDataAsync(new TestNodeUpdateMessage(_sessionUid, platformTestNode, parentTestNodeUid?.ToPlatformTestNodeUid())).ConfigureAwait(false); } return Result.Ok(); diff --git a/src/Adapter/MSTest.Engine/Helpers/ISuccessReason.cs b/src/Adapter/MSTest.Engine/Helpers/ISuccessReason.cs index e9f739f4e5..5952b324bf 100644 --- a/src/Adapter/MSTest.Engine/Helpers/ISuccessReason.cs +++ b/src/Adapter/MSTest.Engine/Helpers/ISuccessReason.cs @@ -3,6 +3,4 @@ namespace Microsoft.Testing.Framework; -internal interface ISuccessReason : IReason -{ -} +internal interface ISuccessReason : IReason; diff --git a/src/Adapter/MSTest.Engine/Helpers/IWarningReason.cs b/src/Adapter/MSTest.Engine/Helpers/IWarningReason.cs index 7b38a5ea9c..e5a50b9aba 100644 --- a/src/Adapter/MSTest.Engine/Helpers/IWarningReason.cs +++ b/src/Adapter/MSTest.Engine/Helpers/IWarningReason.cs @@ -3,6 +3,4 @@ namespace Microsoft.Testing.Framework; -internal interface IWarningReason : IReason -{ -} +internal interface IWarningReason : IReason; diff --git a/src/Adapter/MSTest.Engine/Helpers/Result.cs b/src/Adapter/MSTest.Engine/Helpers/Result.cs index 6f6a70af1d..881c9fa4d1 100644 --- a/src/Adapter/MSTest.Engine/Helpers/Result.cs +++ b/src/Adapter/MSTest.Engine/Helpers/Result.cs @@ -5,7 +5,7 @@ namespace Microsoft.Testing.Framework; internal sealed class Result { - private readonly List _reasons = new(); + private readonly List _reasons = []; private Result() { diff --git a/src/Adapter/MSTest.Engine/MSTest.Engine.csproj b/src/Adapter/MSTest.Engine/MSTest.Engine.csproj index 1f19b9202b..a0ab34f686 100644 --- a/src/Adapter/MSTest.Engine/MSTest.Engine.csproj +++ b/src/Adapter/MSTest.Engine/MSTest.Engine.csproj @@ -13,6 +13,8 @@ $(NoWarn);CS1591 true + + true @@ -28,19 +30,6 @@ true - - - false - - - - - - - - - - @@ -83,23 +72,6 @@ This package provides a new experimental engine for MSTest test framework.]]> - - - - - - - <_TemplateProperties>Version=$(Version) - - - <_TemplateCsproj Include="$(MSBuildProjectDirectory)/RepositoryVersion.cs.template" Destination="$(IntermediateOutputPath)/RepositoryVersion.cs" /> - - - - - - - diff --git a/src/Adapter/MSTest.Engine/TestFramework/TestFramework.cs b/src/Adapter/MSTest.Engine/TestFramework/TestFramework.cs index 1fb61c5f9d..88e34f3fea 100644 --- a/src/Adapter/MSTest.Engine/TestFramework/TestFramework.cs +++ b/src/Adapter/MSTest.Engine/TestFramework/TestFramework.cs @@ -23,8 +23,8 @@ internal sealed class TestFramework : IDisposable, ITestFramework private readonly TestingFrameworkExtension _extension; private readonly CountdownEvent _incomingRequestCounter = new(1); private readonly TestFrameworkEngine _engine; - private readonly List _sessionWarningMessages = new(); - private readonly List _sessionErrorMessages = new(); + private readonly List _sessionWarningMessages = []; + private readonly List _sessionErrorMessages = []; private SessionUid? _sessionId; public TestFramework(TestFrameworkConfiguration testFrameworkConfiguration, ITestNodesBuilder[] testNodesBuilders, TestingFrameworkExtension extension, @@ -47,7 +47,7 @@ public TestFramework(TestFrameworkConfiguration testFrameworkConfiguration, ITes public string Description => _extension.Description; /// - public async Task IsEnabledAsync() => await _extension.IsEnabledAsync(); + public async Task IsEnabledAsync() => await _extension.IsEnabledAsync().ConfigureAwait(false); public Task CreateTestSessionAsync(CreateTestSessionContext context) { @@ -71,7 +71,7 @@ public async Task CloseTestSessionAsync(CloseTestSession { // Ensure we have finished processing all requests. _incomingRequestCounter.Signal(); - await _incomingRequestCounter.WaitAsync(context.CancellationToken); + await _incomingRequestCounter.WaitAsync(context.CancellationToken).ConfigureAwait(false); if (_sessionErrorMessages.Count > 0) { @@ -121,7 +121,7 @@ public async Task ExecuteRequestAsync(ExecuteRequestContext context) throw new InvalidOperationException($"Request type '{context.Request.GetType().FullName}' is not supported"); } - Result result = await _engine.ExecuteRequestAsync(testExecutionRequest, context.MessageBus, context.CancellationToken); + Result result = await _engine.ExecuteRequestAsync(testExecutionRequest, context.MessageBus, context.CancellationToken).ConfigureAwait(false); foreach (IReason reason in result.Reasons) { diff --git a/src/Adapter/MSTest.Engine/TestFramework/TestFrameworkCapabilities.cs b/src/Adapter/MSTest.Engine/TestFramework/TestFrameworkCapabilities.cs index 868a1e63db..c7a853b973 100644 --- a/src/Adapter/MSTest.Engine/TestFramework/TestFrameworkCapabilities.cs +++ b/src/Adapter/MSTest.Engine/TestFramework/TestFrameworkCapabilities.cs @@ -18,7 +18,7 @@ public TestFrameworkCapabilities(ITestNodesBuilder[] testNodesBuilders, IBannerM } public IReadOnlyCollection Capabilities - => new[] { new TestFrameworkCapabilitiesSet(_testNodesBuilders), _bannerMessageOwnerCapability }; + => [new TestFrameworkCapabilitiesSet(_testNodesBuilders), _bannerMessageOwnerCapability]; } internal sealed class TestFrameworkCapabilitiesSet : @@ -39,7 +39,7 @@ public TestFrameworkCapabilitiesSet(ITestNodesBuilder[] testNodesBuilders) public bool IsTrxReportCapabilitySupported { get; } - bool ITestNodesTreeFilterTestFrameworkCapability.IsSupported { get; } = true; + bool ITestNodesTreeFilterTestFrameworkCapability.IsSupported => true; bool ITrxReportCapability.IsSupported => IsTrxReportCapabilitySupported; diff --git a/src/Adapter/MSTest.Engine/TestNodes/FactoryTestNodesBuilder.cs b/src/Adapter/MSTest.Engine/TestNodes/FactoryTestNodesBuilder.cs index 8f2b9bc3b6..eaad6454de 100644 --- a/src/Adapter/MSTest.Engine/TestNodes/FactoryTestNodesBuilder.cs +++ b/src/Adapter/MSTest.Engine/TestNodes/FactoryTestNodesBuilder.cs @@ -15,8 +15,7 @@ public FactoryTestNodesBuilder(Func testNodesFactory) public bool IsSupportingTrxProperties { get; } - IReadOnlyCollection ICapabilities.Capabilities - => Array.Empty(); + IReadOnlyCollection ICapabilities.Capabilities => []; public Task BuildAsync(ITestSessionContext _) => Task.FromResult(_testNodesFactory()); } diff --git a/src/Adapter/MSTest.Engine/TestNodes/FrameworkTestNodeProperties.cs b/src/Adapter/MSTest.Engine/TestNodes/FrameworkTestNodeProperties.cs index 718e6b8b40..3c61f91f6c 100644 --- a/src/Adapter/MSTest.Engine/TestNodes/FrameworkTestNodeProperties.cs +++ b/src/Adapter/MSTest.Engine/TestNodes/FrameworkTestNodeProperties.cs @@ -9,5 +9,5 @@ internal readonly struct FrameworkEngineMetadataProperty() : IProperty { public bool PreventArgumentsExpansion { get; init; } - public string[] UsedFixtureIds { get; init; } = Array.Empty(); + public string[] UsedFixtureIds { get; init; } = []; } diff --git a/src/Adapter/MSTest.Engine/TestNodes/IActionableTestNode.cs b/src/Adapter/MSTest.Engine/TestNodes/IActionableTestNode.cs index 158cd96a6e..2631b535d2 100644 --- a/src/Adapter/MSTest.Engine/TestNodes/IActionableTestNode.cs +++ b/src/Adapter/MSTest.Engine/TestNodes/IActionableTestNode.cs @@ -3,6 +3,4 @@ namespace Microsoft.Testing.Framework; -internal interface IActionableTestNode -{ -} +internal interface IActionableTestNode; diff --git a/src/Adapter/MSTest.Engine/TestNodes/InternalUnsafeActionParameterizedTestNode.cs b/src/Adapter/MSTest.Engine/TestNodes/InternalUnsafeActionParameterizedTestNode.cs index 749b65b3a0..2f2f7ad49f 100644 --- a/src/Adapter/MSTest.Engine/TestNodes/InternalUnsafeActionParameterizedTestNode.cs +++ b/src/Adapter/MSTest.Engine/TestNodes/InternalUnsafeActionParameterizedTestNode.cs @@ -26,7 +26,7 @@ await safeInvoke(() => { Body(testExecutionContext, item); return Task.CompletedTask; - }); + }).ConfigureAwait(false); } } diff --git a/src/Adapter/MSTest.Engine/TestNodes/InternalUnsafeActionTaskParameterizedTestNode.cs b/src/Adapter/MSTest.Engine/TestNodes/InternalUnsafeActionTaskParameterizedTestNode.cs index 28dffaa306..b5a195628c 100644 --- a/src/Adapter/MSTest.Engine/TestNodes/InternalUnsafeActionTaskParameterizedTestNode.cs +++ b/src/Adapter/MSTest.Engine/TestNodes/InternalUnsafeActionTaskParameterizedTestNode.cs @@ -16,17 +16,17 @@ public sealed class InternalUnsafeActionTaskParameterizedTestNode public required Func>> GetArguments { get; init; } - Func> ITaskParameterizedTestNode.GetArguments => async () => await GetArguments(); + Func> ITaskParameterizedTestNode.GetArguments => async () => await GetArguments().ConfigureAwait(false); async Task IParameterizedAsyncActionTestNode.InvokeAsync(ITestExecutionContext testExecutionContext, Func, Task> safeInvoke) { - foreach (TData item in await GetArguments()) + foreach (TData item in await GetArguments().ConfigureAwait(false)) { await safeInvoke(() => { Body(testExecutionContext, item); return Task.CompletedTask; - }); + }).ConfigureAwait(false); } } diff --git a/src/Adapter/MSTest.Engine/TestNodes/InternalUnsafeAsyncActionParameterizedTestNode.cs b/src/Adapter/MSTest.Engine/TestNodes/InternalUnsafeAsyncActionParameterizedTestNode.cs index 3dbc8ace32..84a220e558 100644 --- a/src/Adapter/MSTest.Engine/TestNodes/InternalUnsafeAsyncActionParameterizedTestNode.cs +++ b/src/Adapter/MSTest.Engine/TestNodes/InternalUnsafeAsyncActionParameterizedTestNode.cs @@ -22,7 +22,7 @@ async Task IParameterizedAsyncActionTestNode.InvokeAsync(ITestExecutionContext t { foreach (TData item in GetArguments()) { - await safeInvoke(async () => await Body(testExecutionContext, item)); + await safeInvoke(async () => await Body(testExecutionContext, item).ConfigureAwait(false)).ConfigureAwait(false); } } diff --git a/src/Adapter/MSTest.Engine/TestNodes/InternalUnsafeAsyncActionTaskParameterizedTestNode.cs b/src/Adapter/MSTest.Engine/TestNodes/InternalUnsafeAsyncActionTaskParameterizedTestNode.cs index f27470ba19..1a56446928 100644 --- a/src/Adapter/MSTest.Engine/TestNodes/InternalUnsafeAsyncActionTaskParameterizedTestNode.cs +++ b/src/Adapter/MSTest.Engine/TestNodes/InternalUnsafeAsyncActionTaskParameterizedTestNode.cs @@ -16,13 +16,13 @@ public sealed class InternalUnsafeAsyncActionTaskParameterizedTestNode public required Func>> GetArguments { get; init; } - Func> ITaskParameterizedTestNode.GetArguments => async () => await GetArguments(); + Func> ITaskParameterizedTestNode.GetArguments => async () => await GetArguments().ConfigureAwait(false); async Task IParameterizedAsyncActionTestNode.InvokeAsync(ITestExecutionContext testExecutionContext, Func, Task> safeInvoke) { - foreach (TData item in await GetArguments()) + foreach (TData item in await GetArguments().ConfigureAwait(false)) { - await safeInvoke(async () => await Body(testExecutionContext, item)); + await safeInvoke(async () => await Body(testExecutionContext, item).ConfigureAwait(false)).ConfigureAwait(false); } } diff --git a/src/Adapter/MSTest.Engine/TestNodes/InternalUnsafeAsyncActionTestNode.cs b/src/Adapter/MSTest.Engine/TestNodes/InternalUnsafeAsyncActionTestNode.cs index 5d8a0aa48a..f6abe9f436 100644 --- a/src/Adapter/MSTest.Engine/TestNodes/InternalUnsafeAsyncActionTestNode.cs +++ b/src/Adapter/MSTest.Engine/TestNodes/InternalUnsafeAsyncActionTestNode.cs @@ -11,5 +11,5 @@ public sealed class InternalUnsafeAsyncActionTestNode : TestNode, IAsyncActionTe public required Func Body { get; init; } async Task IAsyncActionTestNode.InvokeAsync(ITestExecutionContext testExecutionContext) - => await Body(testExecutionContext); + => await Body(testExecutionContext).ConfigureAwait(false); } diff --git a/src/Adapter/MSTest.Engine/TestNodes/TestNode.cs b/src/Adapter/MSTest.Engine/TestNodes/TestNode.cs index 58f015c055..8119973fde 100644 --- a/src/Adapter/MSTest.Engine/TestNodes/TestNode.cs +++ b/src/Adapter/MSTest.Engine/TestNodes/TestNode.cs @@ -17,7 +17,7 @@ public class TestNode /// public string? OverriddenEdgeName { get; init; } - public IProperty[] Properties { get; init; } = Array.Empty(); + public IProperty[] Properties { get; init; } = []; - public TestNode[] Tests { get; init; } = Array.Empty(); + public TestNode[] Tests { get; init; } = []; } diff --git a/src/Adapter/MSTest.TestAdapter/MSTestVersion.cs.template b/src/Adapter/MSTest.TestAdapter/BuildInfo.cs.template similarity index 100% rename from src/Adapter/MSTest.TestAdapter/MSTestVersion.cs.template rename to src/Adapter/MSTest.TestAdapter/BuildInfo.cs.template diff --git a/src/Adapter/MSTest.TestAdapter/Execution/ClassCleanupManager.cs b/src/Adapter/MSTest.TestAdapter/Execution/ClassCleanupManager.cs deleted file mode 100644 index fca58e95d8..0000000000 --- a/src/Adapter/MSTest.TestAdapter/Execution/ClassCleanupManager.cs +++ /dev/null @@ -1,91 +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.VisualStudio.TestPlatform.MSTest.TestAdapter.Helpers; -using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel; -using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices; -using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging; -using Microsoft.VisualStudio.TestTools.UnitTesting; - -namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution; - -internal sealed class ClassCleanupManager -{ - private readonly ClassCleanupBehavior? _lifecycleFromMsTest; - private readonly ClassCleanupBehavior _lifecycleFromAssembly; - private readonly ReflectHelper _reflectHelper; - private readonly ConcurrentDictionary> _remainingTestsByClass; - - public ClassCleanupManager( - IEnumerable testsToRun, - ClassCleanupBehavior? lifecycleFromMsTest, - ClassCleanupBehavior lifecycleFromAssembly, - ReflectHelper reflectHelper) - { - IEnumerable runnableTests = testsToRun.Where(t => t.Traits is null || !t.Traits.Any(t => t.Name == Constants.FixturesTestTrait)); - _remainingTestsByClass = - new(runnableTests.GroupBy(t => t.TestMethod.FullClassName) - .ToDictionary( - g => g.Key, - g => new List(g.Select(t => t.TestMethod.UniqueName)))); - _lifecycleFromMsTest = lifecycleFromMsTest; - _lifecycleFromAssembly = lifecycleFromAssembly; - _reflectHelper = reflectHelper; - } - - public bool ShouldRunEndOfAssemblyCleanup { get; private set; } - - public void MarkTestComplete(TestMethodInfo testMethodInfo, TestMethod testMethod, out bool shouldRunEndOfClassCleanup) - { - shouldRunEndOfClassCleanup = false; - if (!_remainingTestsByClass.TryGetValue(testMethodInfo.TestClassName, out List? testsByClass)) - { - return; - } - - lock (testsByClass) - { - testsByClass.Remove(testMethod.UniqueName); - if (testsByClass.Count == 0) - { - _remainingTestsByClass.TryRemove(testMethodInfo.TestClassName, out _); - if (testMethodInfo.Parent.HasExecutableCleanupMethod) - { - ClassCleanupBehavior cleanupLifecycle = _reflectHelper.GetClassCleanupBehavior(testMethodInfo.Parent) - ?? _lifecycleFromMsTest - ?? _lifecycleFromAssembly; - - shouldRunEndOfClassCleanup = cleanupLifecycle == ClassCleanupBehavior.EndOfClass; - } - } - - ShouldRunEndOfAssemblyCleanup = _remainingTestsByClass.IsEmpty; - } - } - - internal static void ForceCleanup(TypeCache typeCache, IDictionary sourceLevelParameters, IMessageLogger logger) - { - using var writer = new ThreadSafeStringWriter(CultureInfo.InvariantCulture, "context"); - TestContext testContext = new TestContextImplementation(null, writer, sourceLevelParameters, logger); - IEnumerable classInfoCache = typeCache.ClassInfoListWithExecutableCleanupMethods; - LogMessageListener? listener = null; - foreach (TestClassInfo classInfo in classInfoCache) - { - TestFailedException? ex = classInfo.ExecuteClassCleanup(testContext, out listener); - if (ex is not null) - { - throw ex; - } - } - - IEnumerable assemblyInfoCache = typeCache.AssemblyInfoListWithExecutableCleanupMethods; - foreach (TestAssemblyInfo assemblyInfo in assemblyInfoCache) - { - TestFailedException? ex = assemblyInfo.ExecuteAssemblyCleanup(testContext, ref listener); - if (ex is not null) - { - throw ex; - } - } - } -} diff --git a/src/Adapter/MSTest.TestAdapter/Execution/TcmTestPropertiesProvider.cs b/src/Adapter/MSTest.TestAdapter/Execution/TcmTestPropertiesProvider.cs deleted file mode 100644 index 1735b1cf0b..0000000000 --- a/src/Adapter/MSTest.TestAdapter/Execution/TcmTestPropertiesProvider.cs +++ /dev/null @@ -1,50 +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 TestPlatformObjectModel = Microsoft.VisualStudio.TestPlatform.ObjectModel; - -namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution; - -/// -/// Reads and parses the TcmTestProperties in order to populate them in TestRunParameters. -/// -internal static class TcmTestPropertiesProvider -{ - /// - /// Gets tcm properties from test case. - /// - /// Test case. - /// Tcm properties. - public static IDictionary GetTcmProperties(TestPlatformObjectModel.TestCase? testCase) - { - var tcmProperties = new Dictionary(); - - // Return empty properties when testCase is null or when test case id is zero. - if (testCase == null || - testCase.GetPropertyValue(Constants.TestCaseIdProperty, default) == 0) - { - return tcmProperties; - } - - // Step 1: Add common properties. - tcmProperties[Constants.TestRunIdProperty] = testCase.GetPropertyValue(Constants.TestRunIdProperty, default); - tcmProperties[Constants.TestPlanIdProperty] = testCase.GetPropertyValue(Constants.TestPlanIdProperty, default); - tcmProperties[Constants.BuildConfigurationIdProperty] = testCase.GetPropertyValue(Constants.BuildConfigurationIdProperty, default); - tcmProperties[Constants.BuildDirectoryProperty] = testCase.GetPropertyValue(Constants.BuildDirectoryProperty, default); - tcmProperties[Constants.BuildFlavorProperty] = testCase.GetPropertyValue(Constants.BuildFlavorProperty, default); - tcmProperties[Constants.BuildNumberProperty] = testCase.GetPropertyValue(Constants.BuildNumberProperty, default); - tcmProperties[Constants.BuildPlatformProperty] = testCase.GetPropertyValue(Constants.BuildPlatformProperty, default); - tcmProperties[Constants.BuildUriProperty] = testCase.GetPropertyValue(Constants.BuildUriProperty, default); - tcmProperties[Constants.TfsServerCollectionUrlProperty] = testCase.GetPropertyValue(Constants.TfsServerCollectionUrlProperty, default); - tcmProperties[Constants.TfsTeamProjectProperty] = testCase.GetPropertyValue(Constants.TfsTeamProjectProperty, default); - tcmProperties[Constants.IsInLabEnvironmentProperty] = testCase.GetPropertyValue(Constants.IsInLabEnvironmentProperty, default); - - // Step 2: Add test case specific properties. - tcmProperties[Constants.TestCaseIdProperty] = testCase.GetPropertyValue(Constants.TestCaseIdProperty, default); - tcmProperties[Constants.TestConfigurationIdProperty] = testCase.GetPropertyValue(Constants.TestConfigurationIdProperty, default); - tcmProperties[Constants.TestConfigurationNameProperty] = testCase.GetPropertyValue(Constants.TestConfigurationNameProperty, default); - tcmProperties[Constants.TestPointIdProperty] = testCase.GetPropertyValue(Constants.TestPointIdProperty, default); - - return tcmProperties; - } -} diff --git a/src/Adapter/MSTest.TestAdapter/Execution/TestRunCancellationToken.cs b/src/Adapter/MSTest.TestAdapter/Execution/TestRunCancellationToken.cs deleted file mode 100644 index fbd7b24255..0000000000 --- a/src/Adapter/MSTest.TestAdapter/Execution/TestRunCancellationToken.cs +++ /dev/null @@ -1,78 +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.VisualStudio.TestPlatform.MSTest.TestAdapter; - -/// -/// Cancellation token supporting cancellation of a test run. -/// -#if NET6_0_OR_GREATER -[Obsolete(Constants.PublicTypeObsoleteMessage, DiagnosticId = "MSTESTOBS")] -#else -[Obsolete(Constants.PublicTypeObsoleteMessage)] -#endif -public class TestRunCancellationToken -{ - /// - /// Callbacks to be invoked when canceled. - /// Needs to be a concurrent collection, see https://github.com/microsoft/testfx/issues/3953. - /// - private readonly ConcurrentBag<(Action, object?)> _registeredCallbacks = new(); - - /// - /// Gets a value indicating whether the test run is canceled. - /// - public bool Canceled - { - get; - private set - { - bool previousValue = field; - field = value; - - if (!previousValue && value) - { - foreach ((Action callBack, object? state) in _registeredCallbacks) - { - callBack.Invoke(state); - } - } - } - } - - /// - /// Cancels the execution of a test run. - /// - public void Cancel() => Canceled = true; - - /// - /// Registers a callback method to be invoked when canceled. - /// - /// Callback delegate for handling cancellation. - public void Register(Action callback) => _registeredCallbacks.Add((_ => callback(), null)); - - internal void Register(Action callback, object? state) => _registeredCallbacks.Add((callback, state)); - - /// - /// Unregister the callback method. - /// - public void Unregister() -#if NETCOREAPP || WINDOWS_UWP - => _registeredCallbacks.Clear(); -#else - { - while (!_registeredCallbacks.IsEmpty) - { - _ = _registeredCallbacks.TryTake(out _); - } - } -#endif - - internal void ThrowIfCancellationRequested() - { - if (Canceled) - { - throw new OperationCanceledException(); - } - } -} diff --git a/src/Adapter/MSTest.TestAdapter/Extensions/ExceptionExtensions.cs b/src/Adapter/MSTest.TestAdapter/Extensions/ExceptionExtensions.cs deleted file mode 100644 index 648fbc41db..0000000000 --- a/src/Adapter/MSTest.TestAdapter/Extensions/ExceptionExtensions.cs +++ /dev/null @@ -1,87 +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.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution; -using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel; -using Microsoft.VisualStudio.TestTools.UnitTesting; - -using UTF = Microsoft.VisualStudio.TestTools.UnitTesting; - -namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Extensions; - -/// -/// Extensions to type. -/// -internal static class ExceptionExtensions -{ - /// - /// TargetInvocationException and TypeInitializationException do not carry any useful information - /// to the user. Find the first inner exception that has useful information. - /// - internal static Exception GetRealException(this Exception exception) - { - // TargetInvocationException: Because .NET Framework wraps method.Invoke() into TargetInvocationException. - // TypeInitializationException: Because AssemblyInitialize is static, and often helpers that are also static - // are used to implement it, and they fail in constructor. - while (exception is TargetInvocationException or TypeInitializationException - && exception.InnerException is not null) - { - exception = exception.InnerException; - } - - return exception; - } - - /// - /// Get the exception message if available, empty otherwise. - /// - /// An object. - /// Exception message. - internal static string TryGetMessage(this Exception? exception) - { - if (exception == null) - { - return string.Format(CultureInfo.CurrentCulture, Resource.UTF_FailedToGetExceptionMessage, "null"); - } - - // It is safe to retrieve an exception message, it should not throw in any case. - return exception.Message ?? string.Empty; - } - - /// - /// Gets the for an exception. - /// - /// An instance. - /// StackTraceInformation for the exception. - internal static StackTraceInformation? TryGetStackTraceInformation(this Exception exception) => !StringEx.IsNullOrEmpty(exception.StackTrace) - ? ExceptionHelper.CreateStackTraceInformation(exception, false, exception.StackTrace) - : null; - - /// - /// Checks whether exception is an Assert exception. - /// - /// An instance. - /// Framework's Outcome depending on type of assertion. - /// Exception message. - /// StackTraceInformation for the exception. - /// True, if Assert exception. False, otherwise. - internal static bool TryGetUnitTestAssertException(this Exception exception, out UTF.UnitTestOutcome outcome, - [NotNullWhen(true)] out string? exceptionMessage, out StackTraceInformation? exceptionStackTrace) - { - if (exception is UnitTestAssertException) - { - outcome = exception is AssertInconclusiveException - ? UTF.UnitTestOutcome.Inconclusive - : UTF.UnitTestOutcome.Failed; - - exceptionMessage = exception.TryGetMessage(); - exceptionStackTrace = exception.TryGetStackTraceInformation(); - return true; - } - - outcome = UTF.UnitTestOutcome.Failed; - exceptionMessage = null; - exceptionStackTrace = null; - return false; - } -} diff --git a/src/Adapter/MSTest.TestAdapter/Friends.cs b/src/Adapter/MSTest.TestAdapter/Friends.cs deleted file mode 100644 index bdfd4316bd..0000000000 --- a/src/Adapter/MSTest.TestAdapter/Friends.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. - -// Friend assemblies -[assembly: InternalsVisibleTo(assemblyName: "Microsoft.VisualStudio.TestPlatform.MSTestAdapter.UnitTests, PublicKey=002400000480000094000000060200000024000052534131000400000100010007d1fa57c4aed9f0a32e84aa0faefd0de9e8fd6aec8f87fb03766c834c99921eb23be79ad9d5dcc1dd9ad236132102900b723cf980957fc4e177108fc607774f29e8320e92ea05ece4e821c0a5efe8f1645c4c0c93c1ab99285d622caa652c1dfad63d745d6f2de5f17e5eaf0fc4963d261c8a12436518206dc093344d5ad293")] -[assembly: InternalsVisibleTo(assemblyName: "DynamicProxyGenAssembly2, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c547cac37abd99c8db225ef2f6c8a3602f3b3606cc9891605d02baa56104f4cfc0734aa39b93bf7852f7d9266654753cc297e7d2edfe0bac1cdcf9f717241550e0a7b191195b7667bb4f64bcb8e2121380fd1d9d46ad2d92d2d15605093924cceaf74c4861eff62abf69b9291ed0a340e113be11e6a7d3113e92484cf7045cc7")] -[assembly: InternalsVisibleTo(assemblyName: "MSTest.VstestConsoleWrapper.IntegrationTests, PublicKey=002400000480000094000000060200000024000052534131000400000100010007d1fa57c4aed9f0a32e84aa0faefd0de9e8fd6aec8f87fb03766c834c99921eb23be79ad9d5dcc1dd9ad236132102900b723cf980957fc4e177108fc607774f29e8320e92ea05ece4e821c0a5efe8f1645c4c0c93c1ab99285d622caa652c1dfad63d745d6f2de5f17e5eaf0fc4963d261c8a12436518206dc093344d5ad293")] -[assembly: InternalsVisibleTo(assemblyName: "MSTest.IntegrationTests, PublicKey=002400000480000094000000060200000024000052534131000400000100010007d1fa57c4aed9f0a32e84aa0faefd0de9e8fd6aec8f87fb03766c834c99921eb23be79ad9d5dcc1dd9ad236132102900b723cf980957fc4e177108fc607774f29e8320e92ea05ece4e821c0a5efe8f1645c4c0c93c1ab99285d622caa652c1dfad63d745d6f2de5f17e5eaf0fc4963d261c8a12436518206dc093344d5ad293")] diff --git a/src/Adapter/MSTest.TestAdapter/Helpers/AttributeComparer.cs b/src/Adapter/MSTest.TestAdapter/Helpers/AttributeComparer.cs deleted file mode 100644 index c82f46d996..0000000000 --- a/src/Adapter/MSTest.TestAdapter/Helpers/AttributeComparer.cs +++ /dev/null @@ -1,14 +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.VisualStudio.TestPlatform.MSTest.TestAdapter.Helpers; - -internal static class AttributeComparer -{ - public static bool IsNonDerived(Attribute attribute) => - // We are explicitly checking the given type *exactly*. We don't want to consider inheritance. - // So, this should NOT be refactored to 'attribute is TAttribute'. - attribute.GetType() == typeof(TAttribute); - - public static bool IsDerived(Attribute attribute) => attribute is TAttribute; -} diff --git a/src/Adapter/MSTest.TestAdapter/Helpers/EnvironmentWrapper.cs b/src/Adapter/MSTest.TestAdapter/Helpers/EnvironmentWrapper.cs deleted file mode 100644 index 792366d479..0000000000 --- a/src/Adapter/MSTest.TestAdapter/Helpers/EnvironmentWrapper.cs +++ /dev/null @@ -1,9 +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.VisualStudio.TestPlatform.MSTest.TestAdapter.Helpers; - -internal sealed class EnvironmentWrapper : IEnvironment -{ - public string MachineName => Environment.MachineName; -} diff --git a/src/Adapter/MSTest.TestAdapter/Helpers/IEnvironment.cs b/src/Adapter/MSTest.TestAdapter/Helpers/IEnvironment.cs deleted file mode 100644 index 96395c6bc4..0000000000 --- a/src/Adapter/MSTest.TestAdapter/Helpers/IEnvironment.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.VisualStudio.TestPlatform.MSTest.TestAdapter.Helpers; - -/// -/// Interface to abstract environment related information. -/// -internal interface IEnvironment -{ - /// - /// Gets the machine name. - /// - string MachineName { get; } -} diff --git a/src/Adapter/MSTest.TestAdapter/MSTest.TestAdapter.csproj b/src/Adapter/MSTest.TestAdapter/MSTest.TestAdapter.csproj index 9574cbfe6e..2537f7345f 100644 --- a/src/Adapter/MSTest.TestAdapter/MSTest.TestAdapter.csproj +++ b/src/Adapter/MSTest.TestAdapter/MSTest.TestAdapter.csproj @@ -37,12 +37,14 @@ $(NoWarn);NU5127;NU5128;NU5100 + + true Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter - TRACE + $(DefineConstants);TRACE true @@ -61,10 +63,6 @@ - - - - @@ -72,17 +70,6 @@ - - True - True - Resource.resx - - - ResXFileCodeGenerator - Resource.Designer.cs - Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter - Designer - @@ -105,36 +92,16 @@ - - - - - - True - True - Resource.resx - + + - - - <_TemplateProperties>Version=$(Version) - - - <_TemplateCsproj Include="$(MSBuildProjectDirectory)/MSTestVersion.cs.template" Destination="$(IntermediateOutputPath)/MSTestVersion.cs" /> - - - - - - - - - - - + + + + diff --git a/src/Adapter/MSTest.TestAdapter/MSTest.TestAdapter.nuspec b/src/Adapter/MSTest.TestAdapter/MSTest.TestAdapter.nuspec index 513ffc408a..e1388eac5f 100644 --- a/src/Adapter/MSTest.TestAdapter/MSTest.TestAdapter.nuspec +++ b/src/Adapter/MSTest.TestAdapter/MSTest.TestAdapter.nuspec @@ -133,6 +133,13 @@ --> + + + diff --git a/src/Adapter/MSTest.TestAdapter/Properties/AssemblyInfo.cs b/src/Adapter/MSTest.TestAdapter/Properties/AssemblyInfo.cs index 03648cfe6d..1986d819e5 100644 --- a/src/Adapter/MSTest.TestAdapter/Properties/AssemblyInfo.cs +++ b/src/Adapter/MSTest.TestAdapter/Properties/AssemblyInfo.cs @@ -3,5 +3,24 @@ using Microsoft.VisualStudio.TestPlatform; using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter; +using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution; +using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Extensions; +using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel; [assembly: TestExtensionTypes(typeof(MSTestDiscoverer), typeof(MSTestExecutor))] + +#pragma warning disable RS0016 // Add public types and members to the declared API (type forwarding is not public API) +[assembly: TypeForwardedTo(typeof(LogMessageListener))] +[assembly: TypeForwardedTo(typeof(MSTestSettings))] +[assembly: TypeForwardedTo(typeof(RunConfigurationSettings))] +[assembly: TypeForwardedTo(typeof(TestAssemblyInfo))] +[assembly: TypeForwardedTo(typeof(TestClassInfo))] +[assembly: TypeForwardedTo(typeof(TestExecutionManager))] +[assembly: TypeForwardedTo(typeof(TestMethod))] +[assembly: TypeForwardedTo(typeof(TestMethodInfo))] +[assembly: TypeForwardedTo(typeof(TestResultExtensions))] +[assembly: TypeForwardedTo(typeof(TestRunCancellationToken))] +[assembly: TypeForwardedTo(typeof(UnitTestOutcome))] +[assembly: TypeForwardedTo(typeof(UnitTestOutcomeExtensions))] +[assembly: TypeForwardedTo(typeof(UnitTestResult))] +#pragma warning restore RS0016 diff --git a/src/Adapter/MSTest.TestAdapter/PublicAPI/PublicAPI.Shipped.txt b/src/Adapter/MSTest.TestAdapter/PublicAPI/PublicAPI.Shipped.txt index 1a24faebf6..7c85eb0167 100644 --- a/src/Adapter/MSTest.TestAdapter/PublicAPI/PublicAPI.Shipped.txt +++ b/src/Adapter/MSTest.TestAdapter/PublicAPI/PublicAPI.Shipped.txt @@ -1,65 +1,4 @@ #nullable enable -const Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestMethodInfo.TimeoutWhenNotSet = 0 -> int -const Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.MSTestSettings.SettingsName = "MSTest" -> string! -const Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.MSTestSettings.SettingsNameAlias = "MSTestV2" -> string! -const Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.TestMethod.TotalHierarchyLevels = 4 -> int -const Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.RunConfigurationSettings.SettingsName = "RunConfiguration" -> string! -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.LogMessageListener -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.LogMessageListener.DebugTrace.get -> string? -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.LogMessageListener.Dispose() -> void -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.LogMessageListener.GetAndClearDebugTrace() -> string? -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.LogMessageListener.GetAndClearStandardError() -> string? -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.LogMessageListener.GetAndClearStandardOutput() -> string? -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.LogMessageListener.LogMessageListener(bool captureDebugTraces) -> void -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.LogMessageListener.StandardError.get -> string! -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.LogMessageListener.StandardOutput.get -> string! -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestAssemblyInfo -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestAssemblyInfo.AssemblyCleanupMethod.get -> System.Reflection.MethodInfo? -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestAssemblyInfo.AssemblyInitializationException.get -> System.Exception? -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestAssemblyInfo.AssemblyInitializeMethod.get -> System.Reflection.MethodInfo? -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestAssemblyInfo.HasExecutableCleanupMethod.get -> bool -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestAssemblyInfo.IsAssemblyInitializeExecuted.get -> bool -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestAssemblyInfo.RunAssemblyCleanup() -> string? -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestAssemblyInfo.RunAssemblyInitialize(Microsoft.VisualStudio.TestTools.UnitTesting.TestContext! testContext) -> void -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestClassInfo -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestClassInfo.BaseClassCleanupMethodsStack.get -> System.Collections.Generic.Stack! -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestClassInfo.BaseClassInitAndCleanupMethods.get -> System.Collections.Generic.Queue!>! -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestClassInfo.BaseTestCleanupMethodsQueue.get -> System.Collections.Generic.Queue! -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestClassInfo.BaseTestInitializeMethodsQueue.get -> System.Collections.Generic.Queue! -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestClassInfo.ClassAttribute.get -> Microsoft.VisualStudio.TestTools.UnitTesting.TestClassAttribute! -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestClassInfo.ClassCleanupException.get -> System.Exception? -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestClassInfo.ClassCleanupMethod.get -> System.Reflection.MethodInfo? -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestClassInfo.ClassInitializationException.get -> System.Exception? -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestClassInfo.ClassInitializeMethod.get -> System.Reflection.MethodInfo? -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestClassInfo.ClassType.get -> System.Type! -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestClassInfo.Constructor.get -> System.Reflection.ConstructorInfo! -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestClassInfo.HasExecutableCleanupMethod.get -> bool -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestClassInfo.IsClassCleanupExecuted.get -> bool -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestClassInfo.IsClassInitializeExecuted.get -> bool -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestClassInfo.Parent.get -> Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestAssemblyInfo! -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestClassInfo.RunClassCleanup(Microsoft.VisualStudio.TestTools.UnitTesting.ClassCleanupBehavior classCleanupLifecycle = Microsoft.VisualStudio.TestTools.UnitTesting.ClassCleanupBehavior.EndOfAssembly) -> string? -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestClassInfo.RunClassInitialize(Microsoft.VisualStudio.TestTools.UnitTesting.TestContext! testContext) -> void -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestClassInfo.TestCleanupMethod.get -> System.Reflection.MethodInfo? -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestClassInfo.TestContextProperty.get -> System.Reflection.PropertyInfo? -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestClassInfo.TestInitializeMethod.get -> System.Reflection.MethodInfo? -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestExecutionManager -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestExecutionManager.RunTests(System.Collections.Generic.IEnumerable! tests, Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter.IRunContext? runContext, Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter.IFrameworkHandle! frameworkHandle, Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.TestRunCancellationToken! runCancellationToken) -> void -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestExecutionManager.RunTests(System.Collections.Generic.IEnumerable! sources, Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter.IRunContext? runContext, Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter.IFrameworkHandle! frameworkHandle, Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.TestRunCancellationToken! cancellationToken) -> void -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestExecutionManager.TestExecutionManager() -> void -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestMethodInfo -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestMethodInfo.Arguments.get -> object?[]? -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestMethodInfo.GetAllAttributes(bool inherit) -> System.Attribute![]? -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestMethodInfo.GetAttributes(bool inherit) -> TAttributeType![]! -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestMethodInfo.IsRunnable.get -> bool -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestMethodInfo.IsTimeoutSet.get -> bool -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestMethodInfo.MethodInfo.get -> System.Reflection.MethodInfo! -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestMethodInfo.NotRunnableReason.get -> string? -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestMethodInfo.ParameterTypes.get -> System.Reflection.ParameterInfo![]! -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestMethodInfo.ReturnType.get -> System.Type! -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestMethodInfo.TestClassName.get -> string! -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestMethodInfo.TestMethodName.get -> string! -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Extensions.TestResultExtensions -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Extensions.UnitTestOutcomeExtensions Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.MSTestDiscoverer Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.MSTestDiscoverer.DiscoverTests(System.Collections.Generic.IEnumerable! sources, Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter.IDiscoveryContext! discoveryContext, Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging.IMessageLogger! logger, Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter.ITestCaseDiscoverySink! discoverySink) -> void Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.MSTestDiscoverer.MSTestDiscoverer() -> void @@ -70,83 +9,7 @@ Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.MSTestExecutor.RunTests(S Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.MSTestExecutor.RunTests(System.Collections.Generic.IEnumerable? sources, Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter.IRunContext? runContext, Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter.IFrameworkHandle? frameworkHandle) -> void Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.MSTestExecutor.TestExecutionManager.get -> Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestExecutionManager! Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.MSTestExecutor.TestExecutionManager.set -> void -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.MSTestSettings -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.MSTestSettings.CaptureDebugTraces.get -> bool -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.MSTestSettings.ClassCleanupLifecycle.get -> Microsoft.VisualStudio.TestTools.UnitTesting.ClassCleanupBehavior? -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.MSTestSettings.DisableParallelization.get -> bool -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.MSTestSettings.EnableBaseClassTestMethodsFromOtherAssemblies.get -> bool -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.MSTestSettings.ForcedLegacyMode.get -> bool -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.MSTestSettings.MapInconclusiveToFailed.get -> bool -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.MSTestSettings.MapNotRunnableToFailed.get -> bool -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.MSTestSettings.MSTestSettings() -> void -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.MSTestSettings.ParallelizationScope.get -> Microsoft.VisualStudio.TestTools.UnitTesting.ExecutionScope? -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.MSTestSettings.ParallelizationWorkers.get -> int? -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.MSTestSettings.TestSettingsFile.get -> string? -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.MSTestSettings.TestTimeout.get -> int -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.MSTestSettings.TreatClassAndAssemblyCleanupWarningsAsErrors.get -> bool -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.MSTestSettings.TreatDiscoveryWarningsAsErrors.get -> bool -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.TestMethod -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.TestMethod.AssemblyName.get -> string! -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.TestMethod.DeclaringAssemblyName.get -> string? -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.TestMethod.DeclaringAssemblyName.set -> void -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.TestMethod.DeclaringClassFullName.get -> string? -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.TestMethod.DeclaringClassFullName.set -> void -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.TestMethod.FullClassName.get -> string! -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.TestMethod.HasManagedMethodAndTypeProperties.get -> bool -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.TestMethod.Hierarchy.get -> System.Collections.Generic.IReadOnlyCollection! -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.TestMethod.IsAsync.get -> bool -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.TestMethod.ManagedMethodName.get -> string? -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.TestMethod.ManagedTypeName.get -> string? -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.TestMethod.Name.get -> string! -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.TestMethod.TestIdGenerationStrategy.get -> Microsoft.VisualStudio.TestTools.UnitTesting.TestIdGenerationStrategy -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.TestMethod.TestMethod(string! name, string! fullClassName, string! assemblyName, bool isAsync) -> void -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.UnitTestOutcome -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.UnitTestOutcome.Error = 0 -> Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.UnitTestOutcome -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.UnitTestOutcome.Failed = 1 -> Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.UnitTestOutcome -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.UnitTestOutcome.Ignored = 4 -> Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.UnitTestOutcome -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.UnitTestOutcome.Inconclusive = 3 -> Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.UnitTestOutcome -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.UnitTestOutcome.InProgress = 8 -> Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.UnitTestOutcome -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.UnitTestOutcome.NotFound = 7 -> Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.UnitTestOutcome -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.UnitTestOutcome.NotRunnable = 5 -> Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.UnitTestOutcome -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.UnitTestOutcome.Passed = 6 -> Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.UnitTestOutcome -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.UnitTestOutcome.Timeout = 2 -> Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.UnitTestOutcome -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.UnitTestResult -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.UnitTestResult.DatarowIndex.get -> int -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.UnitTestResult.DebugTrace.get -> string? -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.UnitTestResult.DisplayName.get -> string? -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.UnitTestResult.Duration.get -> System.TimeSpan -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.UnitTestResult.ErrorColumnNumber.get -> int -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.UnitTestResult.ErrorFilePath.get -> string? -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.UnitTestResult.ErrorLineNumber.get -> int -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.UnitTestResult.ErrorMessage.get -> string? -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.UnitTestResult.ErrorStackTrace.get -> string? -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.UnitTestResult.ExecutionId.get -> System.Guid -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.UnitTestResult.InnerResultsCount.get -> int -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.UnitTestResult.Outcome.get -> Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.UnitTestOutcome -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.UnitTestResult.ParentExecId.get -> System.Guid -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.UnitTestResult.ResultFiles.get -> System.Collections.Generic.IList? -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.UnitTestResult.StandardError.get -> string? -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.UnitTestResult.StandardOut.get -> string? -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.UnitTestResult.TestContextMessages.get -> string? -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.RunConfigurationSettings -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.RunConfigurationSettings.CollectSourceInformation.get -> bool -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.RunConfigurationSettings.RunConfigurationSettings() -> void -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.TestRunCancellationToken -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.TestRunCancellationToken.Cancel() -> void -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.TestRunCancellationToken.Canceled.get -> bool -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.TestRunCancellationToken.Register(System.Action! callback) -> void -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.TestRunCancellationToken.TestRunCancellationToken() -> void -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.TestRunCancellationToken.Unregister() -> void Microsoft.VisualStudio.TestTools.UnitTesting.TestApplicationBuilderExtensions Microsoft.VisualStudio.TestTools.UnitTesting.TestingPlatformBuilderHook -static Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Extensions.TestResultExtensions.ToUnitTestResults(this Microsoft.VisualStudio.TestTools.UnitTesting.TestResult![]! testResults) -> Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.UnitTestResult![]! -static Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Extensions.UnitTestOutcomeExtensions.ToUnitTestOutcome(this Microsoft.VisualStudio.TestTools.UnitTesting.UnitTestOutcome frameworkTestOutcome) -> Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.UnitTestOutcome -static Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.MSTestSettings.CurrentSettings.get -> Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.MSTestSettings! -static Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.MSTestSettings.IsLegacyScenario(Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging.IMessageLogger! logger) -> bool -static Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.MSTestSettings.PopulateSettings(Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.MSTestSettings! settings) -> void -static Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.MSTestSettings.PopulateSettings(Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter.IDiscoveryContext? context) -> void -static Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.MSTestSettings.RunConfigurationSettings.get -> Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.RunConfigurationSettings! -static Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.RunConfigurationSettings.PopulateSettings(Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter.IDiscoveryContext? context) -> Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.RunConfigurationSettings! static Microsoft.VisualStudio.TestTools.UnitTesting.TestApplicationBuilderExtensions.AddMSTest(this Microsoft.Testing.Platform.Builder.ITestApplicationBuilder! testApplicationBuilder, System.Func!>! getTestAssemblies) -> void static Microsoft.VisualStudio.TestTools.UnitTesting.TestingPlatformBuilderHook.AddExtensions(Microsoft.Testing.Platform.Builder.ITestApplicationBuilder! testApplicationBuilder, string![]! arguments) -> void -virtual Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestMethodInfo.Invoke(object?[]? arguments) -> Microsoft.VisualStudio.TestTools.UnitTesting.TestResult! diff --git a/src/Adapter/MSTest.TestAdapter/PublicAPI/PublicAPI.Unshipped.txt b/src/Adapter/MSTest.TestAdapter/PublicAPI/PublicAPI.Unshipped.txt index 22b19265a6..7dc5c58110 100644 --- a/src/Adapter/MSTest.TestAdapter/PublicAPI/PublicAPI.Unshipped.txt +++ b/src/Adapter/MSTest.TestAdapter/PublicAPI/PublicAPI.Unshipped.txt @@ -1,2 +1 @@ #nullable enable -virtual Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestMethodInfo.InvokeAsync(object?[]? arguments) -> System.Threading.Tasks.Task! diff --git a/src/Adapter/MSTest.TestAdapter/PublicAPI/uwp/PublicAPI.Shipped.txt b/src/Adapter/MSTest.TestAdapter/PublicAPI/uwp/PublicAPI.Shipped.txt index 100104fa6b..078eb58f29 100644 --- a/src/Adapter/MSTest.TestAdapter/PublicAPI/uwp/PublicAPI.Shipped.txt +++ b/src/Adapter/MSTest.TestAdapter/PublicAPI/uwp/PublicAPI.Shipped.txt @@ -1,65 +1,4 @@ #nullable enable -const Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestMethodInfo.TimeoutWhenNotSet = 0 -> int -const Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.MSTestSettings.SettingsName = "MSTest" -> string! -const Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.MSTestSettings.SettingsNameAlias = "MSTestV2" -> string! -const Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.TestMethod.TotalHierarchyLevels = 4 -> int -const Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.RunConfigurationSettings.SettingsName = "RunConfiguration" -> string! -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.LogMessageListener -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.LogMessageListener.DebugTrace.get -> string? -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.LogMessageListener.Dispose() -> void -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.LogMessageListener.GetAndClearDebugTrace() -> string? -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.LogMessageListener.GetAndClearStandardError() -> string? -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.LogMessageListener.GetAndClearStandardOutput() -> string? -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.LogMessageListener.LogMessageListener(bool captureDebugTraces) -> void -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.LogMessageListener.StandardError.get -> string! -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.LogMessageListener.StandardOutput.get -> string! -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestAssemblyInfo -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestAssemblyInfo.AssemblyCleanupMethod.get -> System.Reflection.MethodInfo? -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestAssemblyInfo.AssemblyInitializationException.get -> System.Exception? -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestAssemblyInfo.AssemblyInitializeMethod.get -> System.Reflection.MethodInfo? -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestAssemblyInfo.HasExecutableCleanupMethod.get -> bool -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestAssemblyInfo.IsAssemblyInitializeExecuted.get -> bool -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestAssemblyInfo.RunAssemblyCleanup() -> string? -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestAssemblyInfo.RunAssemblyInitialize(Microsoft.VisualStudio.TestTools.UnitTesting.TestContext! testContext) -> void -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestClassInfo -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestClassInfo.BaseClassCleanupMethodsStack.get -> System.Collections.Generic.Stack! -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestClassInfo.BaseClassInitAndCleanupMethods.get -> System.Collections.Generic.Queue!>! -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestClassInfo.BaseTestCleanupMethodsQueue.get -> System.Collections.Generic.Queue! -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestClassInfo.BaseTestInitializeMethodsQueue.get -> System.Collections.Generic.Queue! -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestClassInfo.ClassAttribute.get -> Microsoft.VisualStudio.TestTools.UnitTesting.TestClassAttribute! -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestClassInfo.ClassCleanupException.get -> System.Exception? -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestClassInfo.ClassCleanupMethod.get -> System.Reflection.MethodInfo? -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestClassInfo.ClassInitializationException.get -> System.Exception? -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestClassInfo.ClassInitializeMethod.get -> System.Reflection.MethodInfo? -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestClassInfo.ClassType.get -> System.Type! -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestClassInfo.Constructor.get -> System.Reflection.ConstructorInfo! -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestClassInfo.HasExecutableCleanupMethod.get -> bool -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestClassInfo.IsClassCleanupExecuted.get -> bool -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestClassInfo.IsClassInitializeExecuted.get -> bool -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestClassInfo.Parent.get -> Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestAssemblyInfo! -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestClassInfo.RunClassCleanup(Microsoft.VisualStudio.TestTools.UnitTesting.ClassCleanupBehavior classCleanupLifecycle = Microsoft.VisualStudio.TestTools.UnitTesting.ClassCleanupBehavior.EndOfAssembly) -> string? -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestClassInfo.RunClassInitialize(Microsoft.VisualStudio.TestTools.UnitTesting.TestContext! testContext) -> void -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestClassInfo.TestCleanupMethod.get -> System.Reflection.MethodInfo? -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestClassInfo.TestContextProperty.get -> System.Reflection.PropertyInfo? -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestClassInfo.TestInitializeMethod.get -> System.Reflection.MethodInfo? -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestExecutionManager -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestExecutionManager.RunTests(System.Collections.Generic.IEnumerable! tests, Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter.IRunContext? runContext, Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter.IFrameworkHandle! frameworkHandle, Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.TestRunCancellationToken! runCancellationToken) -> void -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestExecutionManager.RunTests(System.Collections.Generic.IEnumerable! sources, Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter.IRunContext? runContext, Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter.IFrameworkHandle! frameworkHandle, Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.TestRunCancellationToken! cancellationToken) -> void -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestExecutionManager.TestExecutionManager() -> void -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestMethodInfo -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestMethodInfo.Arguments.get -> object?[]? -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestMethodInfo.GetAllAttributes(bool inherit) -> System.Attribute![]? -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestMethodInfo.GetAttributes(bool inherit) -> TAttributeType![]! -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestMethodInfo.IsRunnable.get -> bool -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestMethodInfo.IsTimeoutSet.get -> bool -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestMethodInfo.MethodInfo.get -> System.Reflection.MethodInfo! -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestMethodInfo.NotRunnableReason.get -> string? -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestMethodInfo.ParameterTypes.get -> System.Reflection.ParameterInfo![]! -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestMethodInfo.ReturnType.get -> System.Type! -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestMethodInfo.TestClassName.get -> string! -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestMethodInfo.TestMethodName.get -> string! -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Extensions.TestResultExtensions -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Extensions.UnitTestOutcomeExtensions Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.MSTestDiscoverer Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.MSTestDiscoverer.DiscoverTests(System.Collections.Generic.IEnumerable! sources, Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter.IDiscoveryContext! discoveryContext, Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging.IMessageLogger! logger, Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter.ITestCaseDiscoverySink! discoverySink) -> void Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.MSTestDiscoverer.MSTestDiscoverer() -> void @@ -70,79 +9,3 @@ Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.MSTestExecutor.RunTests(S Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.MSTestExecutor.RunTests(System.Collections.Generic.IEnumerable? sources, Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter.IRunContext? runContext, Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter.IFrameworkHandle? frameworkHandle) -> void Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.MSTestExecutor.TestExecutionManager.get -> Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestExecutionManager! Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.MSTestExecutor.TestExecutionManager.set -> void -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.MSTestSettings -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.MSTestSettings.CaptureDebugTraces.get -> bool -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.MSTestSettings.ClassCleanupLifecycle.get -> Microsoft.VisualStudio.TestTools.UnitTesting.ClassCleanupBehavior? -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.MSTestSettings.DisableParallelization.get -> bool -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.MSTestSettings.EnableBaseClassTestMethodsFromOtherAssemblies.get -> bool -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.MSTestSettings.ForcedLegacyMode.get -> bool -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.MSTestSettings.MapInconclusiveToFailed.get -> bool -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.MSTestSettings.MapNotRunnableToFailed.get -> bool -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.MSTestSettings.MSTestSettings() -> void -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.MSTestSettings.ParallelizationScope.get -> Microsoft.VisualStudio.TestTools.UnitTesting.ExecutionScope? -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.MSTestSettings.ParallelizationWorkers.get -> int? -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.MSTestSettings.TestSettingsFile.get -> string? -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.MSTestSettings.TestTimeout.get -> int -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.MSTestSettings.TreatClassAndAssemblyCleanupWarningsAsErrors.get -> bool -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.MSTestSettings.TreatDiscoveryWarningsAsErrors.get -> bool -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.TestMethod -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.TestMethod.AssemblyName.get -> string! -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.TestMethod.DeclaringAssemblyName.get -> string? -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.TestMethod.DeclaringAssemblyName.set -> void -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.TestMethod.DeclaringClassFullName.get -> string? -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.TestMethod.DeclaringClassFullName.set -> void -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.TestMethod.FullClassName.get -> string! -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.TestMethod.HasManagedMethodAndTypeProperties.get -> bool -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.TestMethod.Hierarchy.get -> System.Collections.Generic.IReadOnlyCollection! -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.TestMethod.IsAsync.get -> bool -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.TestMethod.ManagedMethodName.get -> string? -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.TestMethod.ManagedTypeName.get -> string? -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.TestMethod.Name.get -> string! -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.TestMethod.TestIdGenerationStrategy.get -> Microsoft.VisualStudio.TestTools.UnitTesting.TestIdGenerationStrategy -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.TestMethod.TestMethod(string! name, string! fullClassName, string! assemblyName, bool isAsync) -> void -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.UnitTestOutcome -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.UnitTestOutcome.Error = 0 -> Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.UnitTestOutcome -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.UnitTestOutcome.Failed = 1 -> Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.UnitTestOutcome -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.UnitTestOutcome.Ignored = 4 -> Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.UnitTestOutcome -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.UnitTestOutcome.Inconclusive = 3 -> Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.UnitTestOutcome -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.UnitTestOutcome.InProgress = 8 -> Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.UnitTestOutcome -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.UnitTestOutcome.NotFound = 7 -> Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.UnitTestOutcome -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.UnitTestOutcome.NotRunnable = 5 -> Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.UnitTestOutcome -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.UnitTestOutcome.Passed = 6 -> Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.UnitTestOutcome -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.UnitTestOutcome.Timeout = 2 -> Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.UnitTestOutcome -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.UnitTestResult -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.UnitTestResult.DatarowIndex.get -> int -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.UnitTestResult.DebugTrace.get -> string? -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.UnitTestResult.DisplayName.get -> string? -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.UnitTestResult.Duration.get -> System.TimeSpan -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.UnitTestResult.ErrorColumnNumber.get -> int -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.UnitTestResult.ErrorFilePath.get -> string? -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.UnitTestResult.ErrorLineNumber.get -> int -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.UnitTestResult.ErrorMessage.get -> string? -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.UnitTestResult.ErrorStackTrace.get -> string? -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.UnitTestResult.ExecutionId.get -> System.Guid -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.UnitTestResult.InnerResultsCount.get -> int -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.UnitTestResult.Outcome.get -> Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.UnitTestOutcome -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.UnitTestResult.ParentExecId.get -> System.Guid -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.UnitTestResult.ResultFiles.get -> System.Collections.Generic.IList? -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.UnitTestResult.StandardError.get -> string? -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.UnitTestResult.StandardOut.get -> string? -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.UnitTestResult.TestContextMessages.get -> string? -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.RunConfigurationSettings -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.RunConfigurationSettings.CollectSourceInformation.get -> bool -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.RunConfigurationSettings.RunConfigurationSettings() -> void -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.TestRunCancellationToken -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.TestRunCancellationToken.Cancel() -> void -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.TestRunCancellationToken.Canceled.get -> bool -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.TestRunCancellationToken.Register(System.Action! callback) -> void -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.TestRunCancellationToken.TestRunCancellationToken() -> void -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.TestRunCancellationToken.Unregister() -> void -static Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Extensions.TestResultExtensions.ToUnitTestResults(this Microsoft.VisualStudio.TestTools.UnitTesting.TestResult![]! testResults) -> Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.UnitTestResult![]! -static Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Extensions.UnitTestOutcomeExtensions.ToUnitTestOutcome(this Microsoft.VisualStudio.TestTools.UnitTesting.UnitTestOutcome frameworkTestOutcome) -> Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.UnitTestOutcome -static Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.MSTestSettings.CurrentSettings.get -> Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.MSTestSettings! -static Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.MSTestSettings.IsLegacyScenario(Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging.IMessageLogger! logger) -> bool -static Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.MSTestSettings.PopulateSettings(Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.MSTestSettings! settings) -> void -static Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.MSTestSettings.PopulateSettings(Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter.IDiscoveryContext? context) -> void -static Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.MSTestSettings.RunConfigurationSettings.get -> Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.RunConfigurationSettings! -static Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.RunConfigurationSettings.PopulateSettings(Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter.IDiscoveryContext? context) -> Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.RunConfigurationSettings! -virtual Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestMethodInfo.Invoke(object?[]? arguments) -> Microsoft.VisualStudio.TestTools.UnitTesting.TestResult! diff --git a/src/Adapter/MSTest.TestAdapter/PublicAPI/uwp/PublicAPI.Unshipped.txt b/src/Adapter/MSTest.TestAdapter/PublicAPI/uwp/PublicAPI.Unshipped.txt index 22b19265a6..7dc5c58110 100644 --- a/src/Adapter/MSTest.TestAdapter/PublicAPI/uwp/PublicAPI.Unshipped.txt +++ b/src/Adapter/MSTest.TestAdapter/PublicAPI/uwp/PublicAPI.Unshipped.txt @@ -1,2 +1 @@ #nullable enable -virtual Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestMethodInfo.InvokeAsync(object?[]? arguments) -> System.Threading.Tasks.Task! diff --git a/src/Adapter/MSTest.TestAdapter/Resources/Resource.Designer.cs b/src/Adapter/MSTest.TestAdapter/Resources/Resource.Designer.cs deleted file mode 100644 index 8daf3f194b..0000000000 --- a/src/Adapter/MSTest.TestAdapter/Resources/Resource.Designer.cs +++ /dev/null @@ -1,888 +0,0 @@ -//------------------------------------------------------------------------------ -// -// 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.VisualStudio.TestPlatform.MSTest.TestAdapter { - 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 Resource { - - private static global::System.Resources.ResourceManager resourceMan; - - private static global::System.Globalization.CultureInfo resourceCulture; - - [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] - internal Resource() { - } - - /// - /// 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.VisualStudio.TestPlatform.MSTest.TestAdapter.Resources.Resource", typeof(Resource).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 Assembly cleanup method '{0}.{1}' timed out after {2}ms. - /// - internal static string AssemblyCleanupTimedOut { - get { - return ResourceManager.GetString("AssemblyCleanupTimedOut", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Assembly cleanup method '{0}.{1}' was canceled. - /// - internal static string AssemblyCleanupWasCancelled { - get { - return ResourceManager.GetString("AssemblyCleanupWasCancelled", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Assembly initialize method '{0}.{1}' timed out after {2}ms. - /// - internal static string AssemblyInitializeTimedOut { - get { - return ResourceManager.GetString("AssemblyInitializeTimedOut", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Assembly initialize method '{0}.{1}' was canceled. - /// - internal static string AssemblyInitializeWasCancelled { - get { - return ResourceManager.GetString("AssemblyInitializeWasCancelled", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to MSTestAdapterV2. - /// - internal static string AttachmentSetDisplayName { - get { - return ResourceManager.GetString("AttachmentSetDisplayName", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Exception occurred while enumerating IDataSource attribute on "{0}.{1}": {2}. - /// - internal static string CannotEnumerateIDataSourceAttribute { - get { - return ResourceManager.GetString("CannotEnumerateIDataSourceAttribute", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Exception occurred while expanding IDataSource rows from attribute on "{0}.{1}": {2}. - /// - internal static string CannotExpandIDataSourceAttribute { - get { - return ResourceManager.GetString("CannotExpandIDataSourceAttribute", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Data on index {0} for "{1}" cannot be serialized. All data provided through "IDataSource" should be serializable. If you need to test non-serializable data sources, please make sure you add "TestDataSourceDiscovery" attribute on your test assembly and set the discovery option to "DuringExecution".. - /// - internal static string CannotExpandIDataSourceAttribute_CannotSerialize { - get { - return ResourceManager.GetString("CannotExpandIDataSourceAttribute_CannotSerialize", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Display name "{2}" on indexes {0} and {1} are duplicate. Display names should be unique.. - /// - internal static string CannotExpandIDataSourceAttribute_DuplicateDisplayName { - get { - return ResourceManager.GetString("CannotExpandIDataSourceAttribute_DuplicateDisplayName", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Cannot run test method '{0}.{1}': Test data doesn't match method parameters. Either the count or types are different. - ///Test expected {2} parameter(s), with types '{3}', - ///but received {4} argument(s), with types '{5}'.. - /// - internal static string CannotRunTestArgumentsMismatchError { - get { - return ResourceManager.GetString("CannotRunTestArgumentsMismatchError", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Cannot run test method '{0}.{1}': Method has parameters, but does not define any test source. Use '[DataRow]', '[DynamicData]', or a custom 'ITestDataSource' data source to provide test data.. - /// - internal static string CannotRunTestMethodNoDataError { - get { - return ResourceManager.GetString("CannotRunTestMethodNoDataError", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Class cleanup method '{0}.{1}' timed out after {2}ms. - /// - internal static string ClassCleanupTimedOut { - get { - return ResourceManager.GetString("ClassCleanupTimedOut", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Class cleanup method '{0}.{1}' was canceled. - /// - internal static string ClassCleanupWasCancelled { - get { - return ResourceManager.GetString("ClassCleanupWasCancelled", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Class initialize method '{0}.{1}' timed out after {2}ms. - /// - internal static string ClassInitializeTimedOut { - get { - return ResourceManager.GetString("ClassInitializeTimedOut", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Class initialize method '{0}.{1}' was canceled. - /// - internal static string ClassInitializeWasCancelled { - get { - return ResourceManager.GetString("ClassInitializeWasCancelled", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to MSTestAdapter failed to discover tests in class '{0}' of assembly '{1}' because {2}.. - /// - internal static string CouldNotInspectTypeDuringDiscovery { - get { - return ResourceManager.GetString("CouldNotInspectTypeDuringDiscovery", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to {0} (Data Row {1}). - /// - internal static string DataDrivenResultDisplayName { - get { - return ResourceManager.GetString("DataDrivenResultDisplayName", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Debug Trace:. - /// - internal static string DebugTraceBanner { - get { - return ResourceManager.GetString("DebugTraceBanner", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to [MSTest][Discovery][{0}] {1}. - /// - internal static string DiscoveryWarning { - get { - return ResourceManager.GetString("DiscoveryWarning", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Both '.runsettings' and '.testconfig.json' files have been detected. Please select only one of these test configuration files.. - /// - internal static string DuplicateConfigurationError { - get { - return ResourceManager.GetString("DuplicateConfigurationError", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to {0}: {1}. - /// - internal static string EnumeratorLoadTypeErrorFormat { - get { - return ResourceManager.GetString("EnumeratorLoadTypeErrorFormat", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to "{0}": (Failed to get exception description due to an exception of type "{1}".. - /// - internal static string ExceptionOccuredWhileGettingTheExceptionDescription { - get { - return ResourceManager.GetString("ExceptionOccuredWhileGettingTheExceptionDescription", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Exceptions thrown:. - /// - internal static string ExceptionsThrown { - get { - return ResourceManager.GetString("ExceptionsThrown", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Test '{0}' was canceled. - /// - internal static string Execution_Test_Cancelled { - get { - return ResourceManager.GetString("Execution_Test_Cancelled", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Test '{0}' timed out after {1}ms. - /// - internal static string Execution_Test_Timeout { - get { - return ResourceManager.GetString("Execution_Test_Timeout", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Getting custom attributes for type {0} threw exception (will ignore and use the reflection way): {1}. - /// - internal static string FailedToGetCustomAttribute { - get { - return ResourceManager.GetString("FailedToGetCustomAttribute", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to The type of the generic parameter '{0}' could not be inferred.. - /// - internal static string GenericParameterCantBeInferred { - get { - return ResourceManager.GetString("GenericParameterCantBeInferred", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to The generic test method '{0}' doesn't have arguments, so the generic parameter cannot be inferred.. - /// - internal static string GenericParameterCantBeInferredBecauseNoArguments { - get { - return ResourceManager.GetString("GenericParameterCantBeInferredBecauseNoArguments", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Found two conflicting types for generic parameter '{0}'. The conflicting types are '{1}' and '{2}'.. - /// - internal static string GenericParameterConflict { - get { - return ResourceManager.GetString("GenericParameterConflict", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Invalid value '{0}' specified for 'ClassCleanupLifecycle'. Supported scopes are {1}.. - /// - internal static string InvalidClassCleanupLifecycleValue { - get { - return ResourceManager.GetString("InvalidClassCleanupLifecycleValue", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Invalid value '{0}' specified for 'Scope'. Supported scopes are {1}.. - /// - internal static string InvalidParallelScopeValue { - get { - return ResourceManager.GetString("InvalidParallelScopeValue", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Invalid value '{0}' specified for 'Workers'. The value should be a non-negative integer.. - /// - internal static string InvalidParallelWorkersValue { - get { - return ResourceManager.GetString("InvalidParallelWorkersValue", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Invalid settings '{0}'. Unexpected XmlAttribute: '{1}'.. - /// - internal static string InvalidSettingsXmlAttribute { - get { - return ResourceManager.GetString("InvalidSettingsXmlAttribute", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Invalid settings '{0}'. Unexpected XmlElement: '{1}'.. - /// - internal static string InvalidSettingsXmlElement { - get { - return ResourceManager.GetString("InvalidSettingsXmlElement", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Invalid value '{0}' for runsettings entry '{1}', setting will be ignored.. - /// - internal static string InvalidValue { - get { - return ResourceManager.GetString("InvalidValue", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Warning : A testsettings file or a vsmdi file is not supported with the MSTest V2 Adapter.. - /// - internal static string LegacyScenariosNotSupportedWarning { - get { - return ResourceManager.GetString("LegacyScenariosNotSupportedWarning", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to An older version of MSTestV2 package is loaded in assembly, test discovery might fail to discover all data tests if they depend on `.runsettings` file.. - /// - internal static string OlderTFMVersionFound { - get { - return ResourceManager.GetString("OlderTFMVersionFound", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Running tests in any of the provided sources is not supported for the selected platform. - /// - internal static string SourcesNotSupported { - get { - return ResourceManager.GetString("SourcesNotSupported", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Runsettings entry '<ExecutionApartmentState>STA</ExecutionApartmentState>' is not supported on non-Windows OSes.. - /// - internal static string STAIsOnlySupportedOnWindowsWarning { - get { - return ResourceManager.GetString("STAIsOnlySupportedOnWindowsWarning", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Failed to discover tests from assembly {0}. Reason:{1}. - /// - internal static string TestAssembly_AssemblyDiscoveryFailure { - get { - return ResourceManager.GetString("TestAssembly_AssemblyDiscoveryFailure", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to File does not exist: {0}. - /// - internal static string TestAssembly_FileDoesNotExist { - get { - return ResourceManager.GetString("TestAssembly_FileDoesNotExist", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Test cleanup method '{0}.{1}' timed out after {2}ms. - /// - internal static string TestCleanupTimedOut { - get { - return ResourceManager.GetString("TestCleanupTimedOut", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Test cleanup method '{0}.{1}' was canceled. - /// - internal static string TestCleanupWasCancelled { - get { - return ResourceManager.GetString("TestCleanupWasCancelled", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to TestContext cannot be Null.. - /// - internal static string TestContextIsNull { - get { - return ResourceManager.GetString("TestContextIsNull", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to TestContext Messages:. - /// - internal static string TestContextMessageBanner { - get { - return ResourceManager.GetString("TestContextMessageBanner", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Test initialize method '{0}.{1}' timed out after {2}ms. - /// - internal static string TestInitializeTimedOut { - get { - return ResourceManager.GetString("TestInitializeTimedOut", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Test initialize method '{0}.{1}' was canceled. - /// - internal static string TestInitializeWasCancelled { - get { - return ResourceManager.GetString("TestInitializeWasCancelled", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Test method {0} was not found.. - /// - internal static string TestNotFound { - get { - return ResourceManager.GetString("TestNotFound", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Test Parallelization enabled for {0} (Workers: {1}, Scope: {2}). - /// - internal static string TestParallelizationBanner { - get { - return ResourceManager.GetString("TestParallelizationBanner", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Unable to load types from the test source '{0}'. Some or all of the tests in this source may not be discovered. - ///Error: {1}. - /// - internal static string TypeLoadFailed { - get { - return ResourceManager.GetString("TypeLoadFailed", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Assembly Cleanup method {0}.{1} failed. Error Message: {2}. StackTrace: {3}. - /// - internal static string UTA_AssemblyCleanupMethodWasUnsuccesful { - get { - return ResourceManager.GetString("UTA_AssemblyCleanupMethodWasUnsuccesful", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Assembly Initialization method {0}.{1} threw exception. {2}: {3}. Aborting test execution.. - /// - internal static string UTA_AssemblyInitMethodThrows { - get { - return ResourceManager.GetString("UTA_AssemblyInitMethodThrows", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Class Cleanup method {0}.{1} failed. Error Message: {2}. Stack Trace: {3}. - /// - internal static string UTA_ClassCleanupMethodWasUnsuccesful { - get { - return ResourceManager.GetString("UTA_ClassCleanupMethodWasUnsuccesful", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Class Initialization method {0}.{1} threw exception. {2}: {3}.. - /// - internal static string UTA_ClassInitMethodThrows { - get { - return ResourceManager.GetString("UTA_ClassInitMethodThrows", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Method {0}.{1} has wrong signature. The method must be static, public, does not return a value and should not take any parameter. Additionally, if you are using async-await in method then return-type must be 'Task' or 'ValueTask'.. - /// - internal static string UTA_ClassOrAssemblyCleanupMethodHasWrongSignature { - get { - return ResourceManager.GetString("UTA_ClassOrAssemblyCleanupMethodHasWrongSignature", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Method {0}.{1} has wrong signature. The method must be static, public, does not return a value and should take a single parameter of type TestContext. Additionally, if you are using async-await in method then return-type must be 'Task' or 'ValueTask'.. - /// - internal static string UTA_ClassOrAssemblyInitializeMethodHasWrongSignature { - get { - return ResourceManager.GetString("UTA_ClassOrAssemblyInitializeMethodHasWrongSignature", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to TestCleanup method {0}.{1} threw exception. {2}.. - /// - internal static string UTA_CleanupMethodThrows { - get { - return ResourceManager.GetString("UTA_CleanupMethodThrows", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Error calling Test Cleanup method for test class {0}: {1}. - /// - internal static string UTA_CleanupMethodThrowsGeneralError { - get { - return ResourceManager.GetString("UTA_CleanupMethodThrowsGeneralError", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to TestCleanup Stack Trace. - /// - internal static string UTA_CleanupStackTrace { - get { - return ResourceManager.GetString("UTA_CleanupStackTrace", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to --- End of inner exception stack trace ---. - /// - internal static string UTA_EndOfInnerExceptionTrace { - get { - return ResourceManager.GetString("UTA_EndOfInnerExceptionTrace", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to UTA007: Method {1} defined in class {0} does not have correct signature. Test method marked with the [TestMethod] attribute must be non-static, public, return-type as void and should not take any parameter. Example: public void Test.Class1.Test(). Additionally, if you are using async-await in test method then return-type must be 'Task' or 'ValueTask'. Example: public async Task Test.Class1.Test2(). - /// - internal static string UTA_ErrorIncorrectTestMethodSignature { - get { - return ResourceManager.GetString("UTA_ErrorIncorrectTestMethodSignature", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to UTA031: class {0} does not have valid TestContext property. TestContext must be of type TestContext, must be non-static, and must be public. For example: public TestContext TestContext.. - /// - internal static string UTA_ErrorInValidTestContextSignature { - get { - return ResourceManager.GetString("UTA_ErrorInValidTestContextSignature", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to UTA054: {0}.{1} has invalid Timeout attribute. The timeout must be an integer value greater than 0.. - /// - internal static string UTA_ErrorInvalidTimeout { - get { - return ResourceManager.GetString("UTA_ErrorInvalidTimeout", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to UTA014: {0}: Cannot define more than one method with the AssemblyCleanup attribute inside an assembly.. - /// - internal static string UTA_ErrorMultiAssemblyClean { - get { - return ResourceManager.GetString("UTA_ErrorMultiAssemblyClean", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to UTA013: {0}: Cannot define more than one method with the AssemblyInitialize attribute inside an assembly.. - /// - internal static string UTA_ErrorMultiAssemblyInit { - get { - return ResourceManager.GetString("UTA_ErrorMultiAssemblyInit", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to UTA026: {0}: Cannot define more than one method with the ClassCleanup attribute inside a class.. - /// - internal static string UTA_ErrorMultiClassClean { - get { - return ResourceManager.GetString("UTA_ErrorMultiClassClean", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to UTA025: {0}: Cannot define more than one method with the ClassInitialize attribute inside a class.. - /// - internal static string UTA_ErrorMultiClassInit { - get { - return ResourceManager.GetString("UTA_ErrorMultiClassInit", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to UTA024: {0}: Cannot define more than one method with the TestCleanup attribute.. - /// - internal static string UTA_ErrorMultiClean { - get { - return ResourceManager.GetString("UTA_ErrorMultiClean", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to UTA018: {0}: Cannot define more than one method with the TestInitialize attribute.. - /// - internal static string UTA_ErrorMultiInit { - get { - return ResourceManager.GetString("UTA_ErrorMultiInit", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to UTA001: TestClass attribute defined on non-public class {0}. - /// - internal static string UTA_ErrorNonPublicTestClass { - get { - return ResourceManager.GetString("UTA_ErrorNonPublicTestClass", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to UTA023: {0}: Cannot define predefined property {2} on method {1}.. - /// - internal static string UTA_ErrorPredefinedTestProperty { - get { - return ResourceManager.GetString("UTA_ErrorPredefinedTestProperty", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to TestClass attribute defined on generic non-abstract class {0}. - /// - internal static string UTA_ErrorTestClassIsGenericNonAbstract { - get { - return ResourceManager.GetString("UTA_ErrorTestClassIsGenericNonAbstract", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to UTA021: {0}: Null or empty custom property defined on method {1}. The custom property must have a valid name.. - /// - internal static string UTA_ErrorTestPropertyNullOrEmpty { - get { - return ResourceManager.GetString("UTA_ErrorTestPropertyNullOrEmpty", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to An unhandled exception was thrown by the 'Execute' method. Please report this error to the author of the attribute '{0}'. - ///{1}. - /// - internal static string UTA_ExecuteThrewException { - get { - return ResourceManager.GetString("UTA_ExecuteThrewException", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to The ExpectedException attribute defined on test method {0}.{1} threw an exception during construction. - ///{2}. - /// - internal static string UTA_ExpectedExceptionAttributeConstructionException { - get { - return ResourceManager.GetString("UTA_ExpectedExceptionAttributeConstructionException", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Failed to obtain the exception thrown by test method {0}.{1}.. - /// - internal static string UTA_FailedToGetTestMethodException { - get { - return ResourceManager.GetString("UTA_FailedToGetTestMethodException", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Initialization method {0}.{1} threw exception. {2}.. - /// - internal static string UTA_InitMethodThrows { - get { - return ResourceManager.GetString("UTA_InitMethodThrows", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Unable to create instance of class {0}. Error: {1}.. - /// - internal static string UTA_InstanceCreationError { - get { - return ResourceManager.GetString("UTA_InstanceCreationError", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Method {0}.{1} does not exist.. - /// - internal static string UTA_MethodDoesNotExists { - get { - return ResourceManager.GetString("UTA_MethodDoesNotExists", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to The test method {0}.{1} has multiple attributes derived from '{2}' defined on it. Only one such attribute is allowed.. - /// - internal static string UTA_MultipleAttributesOnTestMethod { - get { - return ResourceManager.GetString("UTA_MultipleAttributesOnTestMethod", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Error in executing test. No result returned by extension. If using extension of TestMethodAttribute then please contact vendor.. - /// - internal static string UTA_NoTestResult { - get { - return ResourceManager.GetString("UTA_NoTestResult", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Cannot find a valid constructor for test class '{0}'. Valid constructors are 'public' and either parameterless or with one parameter of type 'TestContext'.. - /// - internal static string UTA_NoValidConstructor { - get { - return ResourceManager.GetString("UTA_NoValidConstructor", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Unable to find property {0}.TestContext. Error:{1}.. - /// - internal static string UTA_TestContextLoadError { - get { - return ResourceManager.GetString("UTA_TestContextLoadError", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Unable to set TestContext property for the class {0}. Error: {1}.. - /// - internal static string UTA_TestContextSetError { - get { - return ResourceManager.GetString("UTA_TestContextSetError", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to The {0}.TestContext has incorrect type.. - /// - internal static string UTA_TestContextTypeMismatchLoadError { - get { - return ResourceManager.GetString("UTA_TestContextTypeMismatchLoadError", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Method {0}.{1} has wrong signature. The method must be non-static, public, does not return a value and should not take any parameter. Additionally, if you are using async-await in method then return-type must be 'Task' or 'ValueTask'.. - /// - internal static string UTA_TestInitializeAndCleanupMethodHasWrongSignature { - get { - return ResourceManager.GetString("UTA_TestInitializeAndCleanupMethodHasWrongSignature", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Test method {0}.{1} threw exception: - ///{2}. - /// - internal static string UTA_TestMethodThrows { - get { - return ResourceManager.GetString("UTA_TestMethodThrows", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Unable to get type {0}. Error: {1}.. - /// - internal static string UTA_TypeLoadError { - get { - return ResourceManager.GetString("UTA_TypeLoadError", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to The called code threw an exception that was caught, but the exception value was null. - /// - internal static string UTA_UserCodeThrewNullValueException { - get { - return ResourceManager.GetString("UTA_UserCodeThrewNullValueException", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to {0} For UWP projects, if you are using UI objects in test consider using [UITestMethod] attribute instead of [TestMethod] to execute test in UI thread.. - /// - internal static string UTA_WrongThread { - get { - return ResourceManager.GetString("UTA_WrongThread", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to (Failed to get the message for an exception of type {0} due to an exception.). - /// - internal static string UTF_FailedToGetExceptionMessage { - get { - return ResourceManager.GetString("UTF_FailedToGetExceptionMessage", resourceCulture); - } - } - } -} diff --git a/src/Adapter/MSTest.TestAdapter/Resources/Resource.resx b/src/Adapter/MSTest.TestAdapter/Resources/Resource.resx deleted file mode 100644 index 2d8bfb92aa..0000000000 --- a/src/Adapter/MSTest.TestAdapter/Resources/Resource.resx +++ /dev/null @@ -1,418 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 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 - - - Test '{0}' timed out after {1}ms - - - Running tests in any of the provided sources is not supported for the selected platform - - - TestCleanup method {0}.{1} threw exception. {2}. - - - --- End of inner exception stack trace --- - - - UTA014: {0}: Cannot define more than one method with the AssemblyCleanup attribute inside an assembly. - - - UTA013: {0}: Cannot define more than one method with the AssemblyInitialize attribute inside an assembly. - - - UTA026: {0}: Cannot define more than one method with the ClassCleanup attribute inside a class. - - - UTA025: {0}: Cannot define more than one method with the ClassInitialize attribute inside a class. - - - UTA024: {0}: Cannot define more than one method with the TestCleanup attribute. - - - UTA018: {0}: Cannot define more than one method with the TestInitialize attribute. - - - Initialization method {0}.{1} threw exception. {2}. - - - Unable to create instance of class {0}. Error: {1}. - - - Unable to set TestContext property for the class {0}. Error: {1}. - - - (Failed to get the message for an exception of type {0} due to an exception.) - - - UTA031: class {0} does not have valid TestContext property. TestContext must be of type TestContext, must be non-static, and must be public. For example: public TestContext TestContext. - - - UTA001: TestClass attribute defined on non-public class {0} - - - MSTestAdapter failed to discover tests in class '{0}' of assembly '{1}' because {2}. - - - {0}: {1} - - - Unable to load types from the test source '{0}'. Some or all of the tests in this source may not be discovered. -Error: {1} - - - File does not exist: {0} - - - UTA007: Method {1} defined in class {0} does not have correct signature. Test method marked with the [TestMethod] attribute must be non-static, public, return-type as void and should not take any parameter. Example: public void Test.Class1.Test(). Additionally, if you are using async-await in test method then return-type must be 'Task' or 'ValueTask'. Example: public async Task Test.Class1.Test2() - - - TestContext cannot be Null. - - - Assembly Cleanup method {0}.{1} failed. Error Message: {2}. StackTrace: {3} - - - Assembly Initialization method {0}.{1} threw exception. {2}: {3}. Aborting test execution. - - - Class Cleanup method {0}.{1} failed. Error Message: {2}. Stack Trace: {3} - - - Class Initialization method {0}.{1} threw exception. {2}: {3}. - - - An unhandled exception was thrown by the 'Execute' method. Please report this error to the author of the attribute '{0}'. -{1} - - - Error in executing test. No result returned by extension. If using extension of TestMethodAttribute then please contact vendor. - - - Method {0}.{1} has wrong signature. The method must be static, public, does not return a value and should not take any parameter. Additionally, if you are using async-await in method then return-type must be 'Task' or 'ValueTask'. - - - Method {0}.{1} has wrong signature. The method must be static, public, does not return a value and should take a single parameter of type TestContext. Additionally, if you are using async-await in method then return-type must be 'Task' or 'ValueTask'. - - - UTA054: {0}.{1} has invalid Timeout attribute. The timeout must be an integer value greater than 0. - - - UTA023: {0}: Cannot define predefined property {2} on method {1}. - - - UTA021: {0}: Null or empty custom property defined on method {1}. The custom property must have a valid name. - - - Method {0}.{1} does not exist. - - - Cannot find a valid constructor for test class '{0}'. Valid constructors are 'public' and either parameterless or with one parameter of type 'TestContext'. - - - Unable to find property {0}.TestContext. Error:{1}. - - - The {0}.TestContext has incorrect type. - - - Method {0}.{1} has wrong signature. The method must be non-static, public, does not return a value and should not take any parameter. Additionally, if you are using async-await in method then return-type must be 'Task' or 'ValueTask'. - - - Unable to get type {0}. Error: {1}. - - - Test method {0} was not found. - - - Debug Trace: - - - Failed to obtain the exception thrown by test method {0}.{1}. - - - Test method {0}.{1} threw exception: -{2} - - - {0} For UWP projects, if you are using UI objects in test consider using [UITestMethod] attribute instead of [TestMethod] to execute test in UI thread. - - - MSTestAdapterV2 - - - Invalid settings '{0}'. Unexpected XmlAttribute: '{1}'. - - - Invalid settings '{0}'. Unexpected XmlElement: '{1}'. - - - {0} (Data Row {1}) - - - The ExpectedException attribute defined on test method {0}.{1} threw an exception during construction. -{2} - - - The test method '{0}.{1}' has multiple attributes derived from '{2}' defined on it. Only one such attribute is allowed. - - - Warning : A testsettings file or a vsmdi file is not supported with the MSTest V2 Adapter. - - - TestContext Messages: - - - Error calling Test Cleanup method for test class {0}: {1} - - - TestCleanup Stack Trace - - - [MSTest][Discovery][{0}] {1} - - - Test Parallelization enabled for {0} (Workers: {1}, Scope: {2}) - `Workers` is a setting name that shouldn't be localized. 'Scope' is a setting name that shouldn't be localized. - - - Invalid value '{0}' specified for 'Scope'. Supported scopes are {1}. - 'Scope' is a setting name that shouldn't be localized. - - - Invalid value '{0}' specified for 'ClassCleanupLifecycle'. Supported scopes are {1}. - 'ClassCleanupLifecycle' is a setting name that shouldn't be localized. - - - Invalid value '{0}' specified for 'Workers'. The value should be a non-negative integer. - `Workers` is a setting name that shouldn't be localized. - - - Failed to discover tests from assembly {0}. Reason:{1} - - - Cannot run test method '{0}.{1}': Method has parameters, but does not define any test source. Use '[DataRow]', '[DynamicData]', or a custom 'ITestDataSource' data source to provide test data. - - - Test '{0}' was canceled - - - Exception occurred while enumerating IDataSource attribute on "{0}.{1}": {2} - {0}: TypeName with namespace, -{1}: Method name, -{2}: Exception details - - - "{0}": (Failed to get exception description due to an exception of type "{1}". - {0}: Type of the original exception that we're trying to get the description of. -{1}: Thrown exception - - - Exceptions thrown: - This is usually precedes by TestAssembly_AssemblyDiscoveryFailure message, and preceded by list of exceptions thrown in a test discovery session. - - - Getting custom attributes for type {0} threw exception (will ignore and use the reflection way): {1} - {0}: Attribute full type name. -{1}: Exception description - - - An older version of MSTestV2 package is loaded in assembly, test discovery might fail to discover all data tests if they depend on `.runsettings` file. - - - The called code threw an exception that was caught, but the exception value was null - - - Exception occurred while expanding IDataSource rows from attribute on "{0}.{1}": {2} - {0}: TypeName with namespace, -{1}: Method name, -{2}: CannotExpandIDataSourceAttribute_DuplicateDisplayName or CannotExpandIDataSourceAttribute_CannotSerialize - - - Display name "{2}" on indexes {0} and {1} are duplicate. Display names should be unique. - {0}, {1}: Zero based index if an element inside of an array -{2}: Test display name. - - - Data on index {0} for "{1}" cannot be serialized. All data provided through "IDataSource" should be serializable. If you need to test non-serializable data sources, please make sure you add "TestDataSourceDiscovery" attribute on your test assembly and set the discovery option to "DuringExecution". - {0}: Zero based index if an element inside of an array, -{1}: Test name - - - Assembly initialize method '{0}.{1}' timed out after {2}ms - - - Assembly initialize method '{0}.{1}' was canceled - - - Class initialize method '{0}.{1}' timed out after {2}ms - - - Class initialize method '{0}.{1}' was canceled - - - TestClass attribute defined on generic non-abstract class {0} - - - Assembly cleanup method '{0}.{1}' timed out after {2}ms - - - Assembly cleanup method '{0}.{1}' was canceled - - - Class cleanup method '{0}.{1}' timed out after {2}ms - - - Class cleanup method '{0}.{1}' was canceled - - - Test cleanup method '{0}.{1}' timed out after {2}ms - - - Test cleanup method '{0}.{1}' was canceled - - - Test initialize method '{0}.{1}' timed out after {2}ms - - - Test initialize method '{0}.{1}' was canceled - - - Runsettings entry '<ExecutionApartmentState>STA</ExecutionApartmentState>' is not supported on non-Windows OSes. - - - Cannot run test method '{0}.{1}': Test data doesn't match method parameters. Either the count or types are different. -Test expected {2} parameter(s), with types '{3}', -but received {4} argument(s), with types '{5}'. - - - Invalid value '{0}' for runsettings entry '{1}', setting will be ignored. - - - Both '.runsettings' and '.testconfig.json' files have been detected. Please select only one of these test configuration files. - - - The type of the generic parameter '{0}' could not be inferred. - - - The generic test method '{0}' doesn't have arguments, so the generic parameter cannot be inferred. - - - Found two conflicting types for generic parameter '{0}'. The conflicting types are '{1}' and '{2}'. - - diff --git a/src/Adapter/MSTest.TestAdapter/Resources/xlf/Resource.cs.xlf b/src/Adapter/MSTest.TestAdapter/Resources/xlf/Resource.cs.xlf deleted file mode 100644 index 53651299cc..0000000000 --- a/src/Adapter/MSTest.TestAdapter/Resources/xlf/Resource.cs.xlf +++ /dev/null @@ -1,482 +0,0 @@ - - - - - - Assembly cleanup method '{0}.{1}' timed out after {2}ms - Po {2} ms vypršel časový limit metody čištění sestavení {0}.{1}. - - - - Assembly cleanup method '{0}.{1}' was canceled - Metoda čištění sestavení {0}.{1} byla zrušena. - - - - Assembly initialize method '{0}.{1}' timed out after {2}ms - Po {2} ms vypršel časový limit metody inicializace sestavení {0}.{1}. - - - - Assembly initialize method '{0}.{1}' was canceled - Metoda inicializace sestavení {0}.{1} byla zrušena. - - - - Cannot run test method '{0}.{1}': Test data doesn't match method parameters. Either the count or types are different. -Test expected {2} parameter(s), with types '{3}', -but received {4} argument(s), with types '{5}'. - Testovací metodu {0}.{1} nejde spustit: Testovací data neodpovídají parametrům metody. Liší se počet nebo typy. -Pro test se očekával tento počet parametrů: {2} s typy {3}, -byl však přijat tento počet argumentů: {4} s typy {5}. - - - - Cannot run test method '{0}.{1}': Method has parameters, but does not define any test source. Use '[DataRow]', '[DynamicData]', or a custom 'ITestDataSource' data source to provide test data. - Nelze spustit testovací metodu {0}.{1}: Metoda má parametry, ale nedefinuje žádný zdroj testu. K poskytování testovacích dat použijte zdroj dat [DataRow] nebo [DynamicData], případně vlastní zdroj dat ITestDataSource. - - - - 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}. - - - - Class cleanup method '{0}.{1}' was canceled - Metoda čištění třídy {0}.{1} byla zrušena. - - - - Class initialize method '{0}.{1}' timed out after {2}ms - Po {2} ms vypršel časový limit metody inicializace třídy {0}.{1}. - - - - Class initialize method '{0}.{1}' was canceled - Metoda inicializace třídy {0}.{1} byla zrušena. - - - - Both '.runsettings' and '.testconfig.json' files have been detected. Please select only one of these test configuration files. - Byly zjištěny soubory .runsettings i .testconfig.json. Vyberte prosím jenom jeden z těchto souborů konfigurace testu. - - - - Test '{0}' timed out after {1}ms - Časový limit '{0}' testu vypršel po {1}ms. - - - - The type of the generic parameter '{0}' could not be inferred. - Typ obecného parametru '{0}' nelze odvodit. - - - - The generic test method '{0}' doesn't have arguments, so the generic parameter cannot be inferred. - Obecná testovací metoda '{0}' nemá argumenty, takže obecný parametr nelze odvodit. - - - - Found two conflicting types for generic parameter '{0}'. The conflicting types are '{1}' and '{2}'. - Byly nalezeny dva konfliktní typy pro obecný parametr '{0}'. Konfliktní typy jsou '{1}' a '{2}'. - - - - Invalid value '{0}' for runsettings entry '{1}', setting will be ignored. - Neplatná hodnota {0} pro položku runsettings {1}. Nastavení bude ignorováno. - - - - Runsettings entry '<ExecutionApartmentState>STA</ExecutionApartmentState>' is not supported on non-Windows OSes. - Položka runsettings <ExecutionApartmentState>STA</ExecutionApartmentState> se v operačních systémech jiných než Windows nepodporuje. - - - - Running tests in any of the provided sources is not supported for the selected platform - Spouštění testů v některém z uvedených zdrojů se pro vybranou platformu nepodporuje. - - - - Test cleanup method '{0}.{1}' timed out after {2}ms - Po {2} ms vypršel časový limit metody čištění testu {0}.{1}. - - - - Test cleanup method '{0}.{1}' was canceled - Metoda čištění testu {0}.{1} byla zrušena. - - - - Test initialize method '{0}.{1}' timed out after {2}ms - Po {2} ms vypršel časový limit metody inicializace testu {0}.{1}. - - - - Test initialize method '{0}.{1}' was canceled - Metoda inicializace testu {0}.{1} byla zrušena. - - - - TestCleanup method {0}.{1} threw exception. {2}. - Metoda TestCleanup {0}.{1} vyvolala výjimku. {2}. - - - - --- End of inner exception stack trace --- - --- Konec trasování zásobníku pro vnitřní výjimku --- - - - - UTA014: {0}: Cannot define more than one method with the AssemblyCleanup attribute inside an assembly. - UTA014: {0}: V jednom sestavení nejde definovat více jak jednu metodu s atributem AssemblyCleanup. - - - - UTA013: {0}: Cannot define more than one method with the AssemblyInitialize attribute inside an assembly. - UTA013: {0}: V jednom sestavení nejde definovat více jak jednu metodu s atributem AssemblyInitialize. - - - - UTA026: {0}: Cannot define more than one method with the ClassCleanup attribute inside a class. - UTA026: {0}: Uvnitř třídy nejde definovat více jak jednu metodu s atributem ClassCleanup. - - - - UTA025: {0}: Cannot define more than one method with the ClassInitialize attribute inside a class. - UTA025: {0}: Uvnitř třídy nejde definovat více jak jednu metodu s atributem ClassInitialize. - - - - UTA024: {0}: Cannot define more than one method with the TestCleanup attribute. - UTA024: {0}: Nejde definovat více jak jednu metodu s atributem TestCleanup. - - - - UTA018: {0}: Cannot define more than one method with the TestInitialize attribute. - UTA018: {0}: Nejde definovat více jak jednu metodu s atributem TestInitialize. - - - - TestClass attribute defined on generic non-abstract class {0} - Atribut TestClass definovaný u obecné neabstraktní třídy {0} - - - - Initialization method {0}.{1} threw exception. {2}. - Inicializační metoda {0}.{1} způsobila výjimku. {2}. - - - - Unable to create instance of class {0}. Error: {1}. - Nepodařilo se vytvořit instanci třídy {0}. Chyba: {1}. - - - - The test method '{0}.{1}' has multiple attributes derived from '{2}' defined on it. Only one such attribute is allowed. - Testovací metoda {0}.{1} má definovaných více atributů odvozených od atributu {2}. Povolený je jenom jeden takový atribut. - - - - Cannot find a valid constructor for test class '{0}'. Valid constructors are 'public' and either parameterless or with one parameter of type 'TestContext'. - Nelze najít platný konstruktor pro testovací třídu {0}. Platné konstruktory jsou public a buď bez parametrů, nebo s jedním parametrem typu TestContext. - - - - Unable to set TestContext property for the class {0}. Error: {1}. - Pro třídu {0} se nepodařilo nastavit vlastnost TestContext. Chyba: {1}. - - - - (Failed to get the message for an exception of type {0} due to an exception.) - (Z důvodu výjimky se nepodařilo získat zprávu o výjimce typu {0}.) - - - - UTA031: class {0} does not have valid TestContext property. TestContext must be of type TestContext, must be non-static, and must be public. For example: public TestContext TestContext. - UTA031: Třída {0} nemá platnou vlastnost TestContext. Vlastnost TestContext musí být typu TestContext, musí být nestatická a musí být veřejná. Například: public TestContext TestContext - - - - UTA001: TestClass attribute defined on non-public class {0} - UTA001: Atribut TestClass se definoval v neveřejné třídě {0}. - - - - MSTestAdapter failed to discover tests in class '{0}' of assembly '{1}' because {2}. - MSTestAdapter nezjistil v třídě {0} sestavení {1} žádný test, protože: {2}. - - - - {0}: {1} - {0}: {1} - - - - Unable to load types from the test source '{0}'. Some or all of the tests in this source may not be discovered. -Error: {1} - Nepovedlo se načíst typy ze zdroje testu {0}. Je možné, že se některé nebo všechny testy v tomto zdroji nezjistily. -Chyba: {1} - - - - File does not exist: {0} - Neexistující soubor: {0} - - - - UTA007: Method {1} defined in class {0} does not have correct signature. Test method marked with the [TestMethod] attribute must be non-static, public, return-type as void and should not take any parameter. Example: public void Test.Class1.Test(). Additionally, if you are using async-await in test method then return-type must be 'Task' or 'ValueTask'. Example: public async Task Test.Class1.Test2() - UTA007: Metoda {1} definovaná ve třídě {0} nemá správný podpis. Testovací metoda označená atributem [TestMethod] nesmí být static ani public, musí mít návratový typ void a nesmí přijímat žádný parametr. Například: public void Test.Class1.Test(). Pokud navíc v testovací metodě používáte operátor async-await, musí být návratový typ Task nebo ValueTask. Například: public async Task Test.Class1.Test2() - - - - TestContext cannot be Null. - TestContext nemůže být Null. - - - - Assembly Cleanup method {0}.{1} failed. Error Message: {2}. StackTrace: {3} - Čisticí metoda sestavení {0}.{1} selhala. Chybová zpráva: {2}. Trasování zásobníku: {3} - - - - Assembly Initialization method {0}.{1} threw exception. {2}: {3}. Aborting test execution. - Inicializační metoda sestavení {0}.{1} vyvolala výjimku. {2}: {3}. Přerušuje se provádění testu. - - - - Class Cleanup method {0}.{1} failed. Error Message: {2}. Stack Trace: {3} - Čisticí metoda třídy {0}.{1} selhala. Chybová zpráva: {2}. Trasování zásobníku: {3} - - - - Class Initialization method {0}.{1} threw exception. {2}: {3}. - Inicializační metoda třídy {0}.{1} vyvolala výjimku. {2}: {3}. - - - - An unhandled exception was thrown by the 'Execute' method. Please report this error to the author of the attribute '{0}'. -{1} - Metoda Execute vyvolala neošetřenou výjimku. Nahlaste prosím tuto chybu autorovi '{0}' atributu. -{1} - - - - Error in executing test. No result returned by extension. If using extension of TestMethodAttribute then please contact vendor. - Při provádění testu došlo k chybě. Rozšíření nevrátilo žádný výsledek. Pokud používáte rozšíření třídy TestMethodAttribute, obraťte se na dodavatele. - - - - Method {0}.{1} has wrong signature. The method must be static, public, does not return a value and should not take any parameter. Additionally, if you are using async-await in method then return-type must be 'Task' or 'ValueTask'. - Metoda {0}.{1} má špatný podpis. Metoda musí být static nebo public, nevrací hodnotu a nesmí přijímat žádný parametr. Pokud navíc v metodě používáte operátor async-await, musí být návratový typ Task nebo ValueTask. - - - - Method {0}.{1} has wrong signature. The method must be static, public, does not return a value and should take a single parameter of type TestContext. Additionally, if you are using async-await in method then return-type must be 'Task' or 'ValueTask'. - Metoda {0}.{1} má špatný podpis. Metoda musí být static nebo public, nevrací hodnotu a musí přijímat jeden parametr typu TestContext. Pokud navíc v metodě používáte operátor async-await, musí být návratový typ Task nebo ValueTask. - - - - UTA054: {0}.{1} has invalid Timeout attribute. The timeout must be an integer value greater than 0. - UTA054: {0}.{1} má neplatný atribut Timeout. Hodnota timeline musí být celé číslo větší než 0. - - - - UTA023: {0}: Cannot define predefined property {2} on method {1}. - UTA023: {0}: V metodě {1} nejde definovat předdefinovanou vlastnost {2}. - - - - UTA021: {0}: Null or empty custom property defined on method {1}. The custom property must have a valid name. - UTA021: {0}: V metodě {1} je definovaná vlastní vlastnost, která je null nebo je prázdná. Vlastní vlastnost musí mít platný název. - - - - Method {0}.{1} does not exist. - Metoda {0}.{1} neexistuje. - - - - Unable to find property {0}.TestContext. Error:{1}. - Nepodařilo se najít vlastnost {0}.TestContext. Chyba:{1}. - - - - The {0}.TestContext has incorrect type. - {0}.TestContext má nesprávný typ. - - - - Method {0}.{1} has wrong signature. The method must be non-static, public, does not return a value and should not take any parameter. Additionally, if you are using async-await in method then return-type must be 'Task' or 'ValueTask'. - Metoda {0}.{1} má špatný podpis. Metoda nesmí být static nebo public, nevrací hodnotu a nesmí přijímat žádný parametr. Pokud navíc v metodě používáte operátor async-await, musí být návratový typ Task nebo ValueTask. - - - - Unable to get type {0}. Error: {1}. - Nepodařilo se získat typ {0}. Chyba: {1}. - - - - Test method {0} was not found. - Testovací metoda {0} se nenašla. - - - - Debug Trace: - Trasování ladění: - - - - Failed to obtain the exception thrown by test method {0}.{1}. - Nepovedlo se získat výjimku vyvolanou testovací metodou {0}.{1}. - - - - Test method {0}.{1} threw exception: -{2} - V testovací metodě {0}.{1} došlo k výjimce: -{2} - - - - {0} For UWP projects, if you are using UI objects in test consider using [UITestMethod] attribute instead of [TestMethod] to execute test in UI thread. - {0}. Pokud v testu používáte objekty uživatelského rozhraní, zvažte u projektů pro platformu UPW použití atributu [UITestMethod] místo atributu [TestMethod], aby se test provedl ve vlákně uživatelského rozhraní. - - - - MSTestAdapterV2 - MSTestAdapterV2 - - - - Invalid settings '{0}'. Unexpected XmlAttribute: '{1}'. - Neplatné nastavení {0}. Neočekávaný XmlAttribute: {1}. - - - - Invalid settings '{0}'. Unexpected XmlElement: '{1}'. - Neplatné nastavení {0}. Neočekávaný XmlElement: {1}. - - - - {0} (Data Row {1}) - {0} (datový řádek {1}) - - - - The ExpectedException attribute defined on test method {0}.{1} threw an exception during construction. -{2} - Atribut ExpectedException definovaný u testovací metody {0}.{1} vyvolal během vytváření výjimku. -{2} - - - - Warning : A testsettings file or a vsmdi file is not supported with the MSTest V2 Adapter. - Upozornění: Adaptér MSTest V2 nepodporuje soubor testsettings ani vsmdi. - - - - TestContext Messages: - Zprávy pro TestContext: - - - - Error calling Test Cleanup method for test class {0}: {1} - Při volání čisticí metody testu pro třídu {0} došlo k chybě: {1} - - - - TestCleanup Stack Trace - Trasování zásobníku čištění testu - - - - [MSTest][Discovery][{0}] {1} - [MSTest][Discovery][{0}] {1} - - - - Test Parallelization enabled for {0} (Workers: {1}, Scope: {2}) - Je povolená paralelizace testu pro {0} (pracovní procesy: {1}, obor: {2}). - `Workers` is a setting name that shouldn't be localized. 'Scope' is a setting name that shouldn't be localized. - - - Invalid value '{0}' specified for 'Scope'. Supported scopes are {1}. - Pro Obor je zadaná neplatná hodnota {0}. Podporované obory jsou {1}. - 'Scope' is a setting name that shouldn't be localized. - - - Invalid value '{0}' specified for 'Workers'. The value should be a non-negative integer. - Pro Pracovní procesy je zadaná neplatná hodnota {0}. Hodnota by měla být nezáporné celé číslo. - `Workers` is a setting name that shouldn't be localized. - - - Failed to discover tests from assembly {0}. Reason:{1} - Nepovedlo se zjistit testy ze sestavení {0}. Důvod:{1} - - - - Test '{0}' was canceled - Testovací '{0}' se zrušila. - - - - Invalid value '{0}' specified for 'ClassCleanupLifecycle'. Supported scopes are {1}. - Pro classCleanupLifecycle byla zadána neplatná hodnota {0}. Podporované obory jsou {1}. - 'ClassCleanupLifecycle' is a setting name that shouldn't be localized. - - - Exception occurred while enumerating IDataSource attribute on "{0}.{1}": {2} - Při vytváření výčtu atributu IDataSource došlo k výjimce. „{0}.{1}“: {2} - {0}: TypeName with namespace, -{1}: Method name, -{2}: Exception details - - - "{0}": (Failed to get exception description due to an exception of type "{1}". - „{0}“: (Nepodařilo se získat popis výjimky z důvodu výjimky typu „{1}“. - {0}: Type of the original exception that we're trying to get the description of. -{1}: Thrown exception - - - Exceptions thrown: - Vyvolané výjimky: - This is usually precedes by TestAssembly_AssemblyDiscoveryFailure message, and preceded by list of exceptions thrown in a test discovery session. - - - Getting custom attributes for type {0} threw exception (will ignore and use the reflection way): {1} - Získání vlastních atributů pro typ {0} vyvolalo výjimku (bude ignorovat a používat způsob reflexe): {1} - {0}: Attribute full type name. -{1}: Exception description - - - An older version of MSTestV2 package is loaded in assembly, test discovery might fail to discover all data tests if they depend on `.runsettings` file. - V sestavení je načtena starší verze balíčku MSTestV2. Zjišťování testů může selhat při zjišťování všech testů dat, pokud jsou závislé na souboru .runsettings. - - - - The called code threw an exception that was caught, but the exception value was null - Volaný kód vyvolal výjimku, která byla zachycena, ale její hodnota byla null. - - - - Exception occurred while expanding IDataSource rows from attribute on "{0}.{1}": {2} - Došlo k výjimce při rozbalování řádků IDataSource z atributu na „{0}.{1}“: {2} - {0}: TypeName with namespace, -{1}: Method name, -{2}: CannotExpandIDataSourceAttribute_DuplicateDisplayName or CannotExpandIDataSourceAttribute_CannotSerialize - - - Display name "{2}" on indexes {0} and {1} are duplicate. Display names should be unique. - Zobrazovaný název „{2}“ u indexů {0} a {1} je duplicitní. Zobrazované názvy by měly být jedinečné. - {0}, {1}: Zero based index if an element inside of an array -{2}: Test display name. - - - Data on index {0} for "{1}" cannot be serialized. All data provided through "IDataSource" should be serializable. If you need to test non-serializable data sources, please make sure you add "TestDataSourceDiscovery" attribute on your test assembly and set the discovery option to "DuringExecution". - Data v {0} indexu pro „{1}“ nelze serializovat. Všechna data poskytnutá prostřednictvím „IDataSource“ by měla být serializovatelná. Pokud potřebujete testovat neserializovatelné zdroje dat, nezapomeňte do testovacího sestavení přidat atribut TestDataSourceDiscovery a nastavit možnost zjišťování na „DuringExecution“. - {0}: Zero based index if an element inside of an array, -{1}: Test name - - - - \ No newline at end of file diff --git a/src/Adapter/MSTest.TestAdapter/Resources/xlf/Resource.de.xlf b/src/Adapter/MSTest.TestAdapter/Resources/xlf/Resource.de.xlf deleted file mode 100644 index 458b366659..0000000000 --- a/src/Adapter/MSTest.TestAdapter/Resources/xlf/Resource.de.xlf +++ /dev/null @@ -1,482 +0,0 @@ - - - - - - Assembly cleanup method '{0}.{1}' timed out after {2}ms - Timeout der Assemblybereinigungsmethode "{0}.{1}" nach {2} ms - - - - Assembly cleanup method '{0}.{1}' was canceled - Die Assemblybereinigungsmethode "{0}.{1}" wurde abgebrochen - - - - Assembly initialize method '{0}.{1}' timed out after {2}ms - Timeout der Assemblyinitialisierungsmethode "{0}.{1}" nach {2} ms - - - - Assembly initialize method '{0}.{1}' was canceled - Die Assemblyinitialisierungsmethode "{0}.{1}" wurde abgebrochen - - - - Cannot run test method '{0}.{1}': Test data doesn't match method parameters. Either the count or types are different. -Test expected {2} parameter(s), with types '{3}', -but received {4} argument(s), with types '{5}'. - Die Testmethode „{0}.{1}“ kann nicht ausgeführt werden: Testdaten stimmen nicht mit Methodenparametern überein. Die Anzahl oder die Typen unterscheiden sich. -Test erwartete {2} Parameter mit den Typen „{3}“, -aber empfing {4} Argument(e) mit den Typen „{5}“. - - - - Cannot run test method '{0}.{1}': Method has parameters, but does not define any test source. Use '[DataRow]', '[DynamicData]', or a custom 'ITestDataSource' data source to provide test data. - Die Testmethode „{0}.{1}“ kann nicht ausgeführt werden: Die Methode verfügt über Parameter, definiert jedoch keine Testquelle. Verwenden Sie „[DataRow]“, „[DynamicData]“ oder eine benutzerdefinierte „ITestDataSource-Datenquelle“, um Testdaten bereitzustellen. - - - - Class cleanup method '{0}.{1}' timed out after {2}ms - Timeout der Klassenbereinigungsmethode "{0}.{1}" nach {2} ms - - - - Class cleanup method '{0}.{1}' was canceled - Die Klassenbereinigungsmethode "{0}.{1}" wurde abgebrochen - - - - Class initialize method '{0}.{1}' timed out after {2}ms - Timeout der Klasseninitialisierungsmethode "{0}.{1}" nach {2} ms - - - - Class initialize method '{0}.{1}' was canceled - Die Initialisierungsmethode "{0}.{1}" der Klasse wurde abgebrochen - - - - Both '.runsettings' and '.testconfig.json' files have been detected. Please select only one of these test configuration files. - Es wurden sowohl die Dateien „.runsettings“ als auch „.testconfig.json“ erkannt. Wählen Sie nur eine dieser Testkonfigurationsdateien aus. - - - - Test '{0}' timed out after {1}ms - Timeout bei test '{0}' nach {1}ms. - - - - The type of the generic parameter '{0}' could not be inferred. - Der Typ des generischen Parameters '{0}' konnte nicht abgeleitet werden. - - - - The generic test method '{0}' doesn't have arguments, so the generic parameter cannot be inferred. - Die generische Testmethode '{0}' hat keine Argumente, daher kann der generische Parameter nicht abgeleitet werden. - - - - Found two conflicting types for generic parameter '{0}'. The conflicting types are '{1}' and '{2}'. - Es wurden zwei in Konflikt stehende Typen für den generischen Parameter '{0}' gefunden. Die in Konflikt stehenden Typen sind '{1}' und '{2}'. - - - - Invalid value '{0}' for runsettings entry '{1}', setting will be ignored. - Ungültiger Wert "{0}" für runsettings-Eintrag "{1}". Die Einstellung wird ignoriert. - - - - Runsettings entry '<ExecutionApartmentState>STA</ExecutionApartmentState>' is not supported on non-Windows OSes. - Der Eintrag "<ExecutionApartmentState>STA</ExecutionApartmentState>" der Ausführungseinstellungen wird auf Nicht-Windows-Betriebssystemen nicht unterstützt. - - - - Running tests in any of the provided sources is not supported for the selected platform - Das Ausführen von Tests in einer der angegebenen Quellen wird für die ausgewählte Plattform nicht unterstützt. - - - - Test cleanup method '{0}.{1}' timed out after {2}ms - Timeout der Testbereinigungsmethode "{0}.{1}" nach {2} ms - - - - Test cleanup method '{0}.{1}' was canceled - Die Testbereinigungsmethode "{0}.{1}" wurde abgebrochen - - - - Test initialize method '{0}.{1}' timed out after {2}ms - Timeout der Testinitialisierungsmethode "{0}.{1}" nach {2} ms - - - - Test initialize method '{0}.{1}' was canceled - Die Testinitialisierungsmethode "{0}.{1}" wurde abgebrochen - - - - TestCleanup method {0}.{1} threw exception. {2}. - Die TestCleanup-Methode "{0}.{1}" hat eine Ausnahme ausgelöst. {2}. - - - - --- End of inner exception stack trace --- - --- Ende der internen Ausnahmestapelüberwachung --- - - - - UTA014: {0}: Cannot define more than one method with the AssemblyCleanup attribute inside an assembly. - UTA014: {0}: Es darf nur eine Methode mit dem Attribut 'AssemblyCleanup' innerhalb einer Assembly definiert werden. - - - - UTA013: {0}: Cannot define more than one method with the AssemblyInitialize attribute inside an assembly. - UTA013: {0}: Es darf nur eine Methode mit dem Attribut 'AssemblyInitialize' innerhalb einer Assembly definiert werden. - - - - UTA026: {0}: Cannot define more than one method with the ClassCleanup attribute inside a class. - UTA026: {0}: Es darf nur eine Methode mit dem Attribut 'ClassCleanup' innerhalb einer Klasse definiert werden. - - - - UTA025: {0}: Cannot define more than one method with the ClassInitialize attribute inside a class. - UTA025: {0}: Es darf nur eine Methode mit dem Attribut 'ClassInitialize' innerhalb einer Klasse definiert werden. - - - - UTA024: {0}: Cannot define more than one method with the TestCleanup attribute. - UTA024: {0}: Es darf nur eine Methode mit dem Attribut 'TestCleanup' definiert werden. - - - - UTA018: {0}: Cannot define more than one method with the TestInitialize attribute. - UTA018: {0}: Es darf nur eine Methode mit dem Attribut 'TestInitialize' definiert werden. - - - - TestClass attribute defined on generic non-abstract class {0} - TestClass-Attribut für generische nicht abstrakte Klasse {0} definiert - - - - Initialization method {0}.{1} threw exception. {2}. - Die Initialisierungsmethode '{0}.{1}' hat eine Ausnahme ausgelöst. {2}. - - - - Unable to create instance of class {0}. Error: {1}. - Es kann keine Instanz der Klasse '{0}' erstellt werden. Fehler: {1}. - - - - The test method '{0}.{1}' has multiple attributes derived from '{2}' defined on it. Only one such attribute is allowed. - Für die Testmethode „{0}.{1}“ sind mehrere Attribute definiert, die von „{2}“ abgeleitet sind. Nur ein einziges solches Attribut ist zulässig. - - - - Cannot find a valid constructor for test class '{0}'. Valid constructors are 'public' and either parameterless or with one parameter of type 'TestContext'. - Es wurde kein gültiger Konstruktor für die Testklasse "{0}" gefunden. Gültige Konstruktoren sind "public" und entweder parameterlos oder mit einem Parameter vom Typ "TestContext". - - - - Unable to set TestContext property for the class {0}. Error: {1}. - Die Eigenschaft 'TestContext' für die Klasse '{0}' kann nicht festgelegt werden. Fehler: {1}. - - - - (Failed to get the message for an exception of type {0} due to an exception.) - (Fehler beim Abrufen der Meldung für eine Ausnahme vom Typ "{0}" aufgrund einer Ausnahme.) - - - - UTA031: class {0} does not have valid TestContext property. TestContext must be of type TestContext, must be non-static, and must be public. For example: public TestContext TestContext. - UTA031: Die Klasse {0} weist keine gültige Eigenschaft TestContext auf. TestContext muss vom Typ TestContext sein, muss nicht statisch und öffentlich sein. Beispiel: 'public TestContext TestContext'. - - - - UTA001: TestClass attribute defined on non-public class {0} - UTA001: Für die nicht öffentliche Klasse '{0}' definiertes Attribut 'TestClass'. - - - - MSTestAdapter failed to discover tests in class '{0}' of assembly '{1}' because {2}. - Fehler von 'MSTestAdapter' beim Ermitteln von Tests in der Klasse "{0}" der Assembly "{1}". Ursache: {2}. - - - - {0}: {1} - {0}: {1} - - - - Unable to load types from the test source '{0}'. Some or all of the tests in this source may not be discovered. -Error: {1} - Fehler beim Laden von Typen aus der Testquelle "{0}". Möglicherweise werden einige oder alle Tests in dieser Quelle nicht ermittelt. -Fehler: {1} - - - - File does not exist: {0} - Die Datei ist nicht vorhanden: {0} - - - - UTA007: Method {1} defined in class {0} does not have correct signature. Test method marked with the [TestMethod] attribute must be non-static, public, return-type as void and should not take any parameter. Example: public void Test.Class1.Test(). Additionally, if you are using async-await in test method then return-type must be 'Task' or 'ValueTask'. Example: public async Task Test.Class1.Test2() - UTA007: Die in der Klasse {0} definierte Methode {1} weist nicht die richtige Signatur auf. Die mit dem [TestMethod]-Attribut markierte Testmethode muss nicht statisch und öffentlich sein, muss den Rückgabetyp „void“ aufweisen und darf keine Parameter annehmen. Beispiel: public void Test.Class1.Test(). Wenn Sie außerdem in der Testmethode „async-await“ verwenden, muss der Rückgabetyp „Task“ oder „ValueTask“ sein. Beispiel: public async Task Test.Class1.Test2() - - - - TestContext cannot be Null. - "TestContext" darf nicht NULL sein. - - - - Assembly Cleanup method {0}.{1} failed. Error Message: {2}. StackTrace: {3} - Fehler bei der Methode "{0}.{1}" für die Assemblybereinigung. Fehlermeldung: {2}. "StackTrace": {3} - - - - Assembly Initialization method {0}.{1} threw exception. {2}: {3}. Aborting test execution. - Die Methode "{0}.{1}" für die Assemblyinitialisierung hat eine Ausnahme ausgelöst. {2}: {3}. Die Ausführung des Tests wird abgebrochen. - - - - Class Cleanup method {0}.{1} failed. Error Message: {2}. Stack Trace: {3} - Fehler bei der Methode "{0}.{1}" für die Klassenbereinigung. Fehlermeldung: {2}. Stapelüberwachung: {3} - - - - Class Initialization method {0}.{1} threw exception. {2}: {3}. - Die Methode "{0}.{1}" für die Klasseninitialisierung hat eine Ausnahme ausgelöst. {2}: {3}. - - - - An unhandled exception was thrown by the 'Execute' method. Please report this error to the author of the attribute '{0}'. -{1} - Von der Methode "Execute" wurde eine nicht behandelte Ausnahme ausgelöst. Melden Sie diesen Fehler dem Autor des Attributs '{0}'. -{1} - - - - Error in executing test. No result returned by extension. If using extension of TestMethodAttribute then please contact vendor. - Fehler beim Ausführen des Tests. Von der Extension wurde kein Ergebnis zurückgegeben. Wenn Sie eine Extension von "TestMethodAttribute" verwenden, wenden Sie sich bitte an den Anbieter. - - - - Method {0}.{1} has wrong signature. The method must be static, public, does not return a value and should not take any parameter. Additionally, if you are using async-await in method then return-type must be 'Task' or 'ValueTask'. - Die Methode „{0}.{1}“ weist eine falsche Signatur auf. Die Methode muss statisch und öffentlich sein. Sie darf keinen Wert zurückgeben und keinen Parameter annehmen. Wenn Sie außerdem in der Methode „async-await“ verwenden, muss der Rückgabetyp „Task“ oder „ValueTask“ sein. - - - - Method {0}.{1} has wrong signature. The method must be static, public, does not return a value and should take a single parameter of type TestContext. Additionally, if you are using async-await in method then return-type must be 'Task' or 'ValueTask'. - Die Methode „{0}.{1}“ weist eine falsche Signatur auf. Die Methode muss statisch und öffentlich sein. Sie darf keinen Wert zurückgeben und muss einen einzigen Parameter des Typs TestContext annehmen. Wenn Sie außerdem in der Methode „async-await“ verwenden, muss der Rückgabetyp „Task“ oder „ValueTask“ sein. - - - - UTA054: {0}.{1} has invalid Timeout attribute. The timeout must be an integer value greater than 0. - UTA054: {0}.{1} weist ein ungültiges Attribut „Timeout“ auf. Timeout muss eine ganze Zahl größer als 0 sein. - - - - UTA023: {0}: Cannot define predefined property {2} on method {1}. - UTA023: {0}: Die vordefinierte Eigenschaft "{2}" kann nicht für die Methode "{1}" definiert werden. - - - - UTA021: {0}: Null or empty custom property defined on method {1}. The custom property must have a valid name. - UTA021: {0}: Für die Methode "{1}" wurde eine benutzerdefinierte Eigenschaft mit dem Wert NULL oder eine benutzerdefinierte leere Eigenschaft definiert. Die benutzerdefinierte Eigenschaft muss einen gültigen Namen aufweisen. - - - - Method {0}.{1} does not exist. - Die Methode "{0}.{1}" ist nicht vorhanden. - - - - Unable to find property {0}.TestContext. Error:{1}. - Die Eigenschaft "{0}.TestContext" wurde nicht gefunden. Fehler: {1}. - - - - The {0}.TestContext has incorrect type. - "{0}.TestContext" weist einen falschen Typ auf. - - - - Method {0}.{1} has wrong signature. The method must be non-static, public, does not return a value and should not take any parameter. Additionally, if you are using async-await in method then return-type must be 'Task' or 'ValueTask'. - Die Methode „{0}.{1}“ weist eine falsche Signatur auf. Die Methode muss nicht statisch und öffentlich sein, und sie darf keinen Wert zurückgeben und keinen Parameter annehmen. Wenn Sie außerdem in der Methode „async-await“ verwenden, muss der Rückgabetyp „Task“ oder „ValueTask“ sein. - - - - Unable to get type {0}. Error: {1}. - Der Typ "{0}" kann nicht abgerufen werden. Fehler: {1}. - - - - Test method {0} was not found. - Die Testmethode "{0}" wurde nicht gefunden. - - - - Debug Trace: - Debugablaufverfolgung: - - - - Failed to obtain the exception thrown by test method {0}.{1}. - Fehler beim Abrufen der von der Testmethode "{0}.{1}" ausgelösten Ausnahme. - - - - Test method {0}.{1} threw exception: -{2} - Die Testmethode "{0}.{1}" hat eine Ausnahme ausgelöst: -{2} - - - - {0} For UWP projects, if you are using UI objects in test consider using [UITestMethod] attribute instead of [TestMethod] to execute test in UI thread. - {0}: Erwägen Sie für UWP-Projekte bei Einsatz von UI-Objekten im Test die Verwendung des [UITestMethod]-Attributs anstelle von "[TestMethod]", um den Test im UI-Thread auszuführen. - - - - MSTestAdapterV2 - MSTestAdapterV2 - - - - Invalid settings '{0}'. Unexpected XmlAttribute: '{1}'. - Ungültige Einstellungen "{0}". Unerwartetes XmlAttribute: "{1}". - - - - Invalid settings '{0}'. Unexpected XmlElement: '{1}'. - Ungültige Einstellungen "{0}". Unerwartetes XmlElement: "{1}". - - - - {0} (Data Row {1}) - {0} (Datenzeile {1}) - - - - The ExpectedException attribute defined on test method {0}.{1} threw an exception during construction. -{2} - Das für die Testmethode "{0}.{1}" definierte ExpectedException-Attribut hat während der Konstruktion eine Ausnahme ausgelöst. -{2} - - - - Warning : A testsettings file or a vsmdi file is not supported with the MSTest V2 Adapter. - Warnung: Eine TESTSETTINGS-Datei oder eine VSMDI-Datei wird vom MSTest-V2-Adapter nicht unterstützt. - - - - TestContext Messages: - TestContext-Meldungen: - - - - Error calling Test Cleanup method for test class {0}: {1} - Fehler beim Aufruf der Testbereinigungsmethode für die Testklasse "{0}": {1} - - - - TestCleanup Stack Trace - TestCleanup-Stapelüberwachung - - - - [MSTest][Discovery][{0}] {1} - [MSTest][Discovery][{0}] {1} - - - - Test Parallelization enabled for {0} (Workers: {1}, Scope: {2}) - Testparallelisierung aktiviert für {0} (Worker: {1}, Bereich: {2}) - `Workers` is a setting name that shouldn't be localized. 'Scope' is a setting name that shouldn't be localized. - - - Invalid value '{0}' specified for 'Scope'. Supported scopes are {1}. - Ungültiger Wert "{0}" für "Scope" angegeben. Unterstützte Bereiche: {1}. - 'Scope' is a setting name that shouldn't be localized. - - - Invalid value '{0}' specified for 'Workers'. The value should be a non-negative integer. - Ungültiger Wert "{0}" für "Workers" angegeben. Der Wert muss eine nicht negative Ganzzahl sein. - `Workers` is a setting name that shouldn't be localized. - - - Failed to discover tests from assembly {0}. Reason:{1} - Fehler beim Ermitteln von Tests aus der Assembly "{0}". Ursache:{1} - - - - Test '{0}' was canceled - Test '{0}' wurde abgebrochen. - - - - Invalid value '{0}' specified for 'ClassCleanupLifecycle'. Supported scopes are {1}. - Für "ClassCleanupLifecycle" wurde ein ungültiger Wert "{0}" angegeben. Unterstützte Bereiche sind {1}. - 'ClassCleanupLifecycle' is a setting name that shouldn't be localized. - - - Exception occurred while enumerating IDataSource attribute on "{0}.{1}": {2} - Ausnahme beim Auflisten des IDataSource-Attributs für "{0}.{1}": {2} - {0}: TypeName with namespace, -{1}: Method name, -{2}: Exception details - - - "{0}": (Failed to get exception description due to an exception of type "{1}". - "{0}": (Fehler beim Abrufen der Ausnahmebeschreibung aufgrund einer Ausnahme vom Typ "{1}". - {0}: Type of the original exception that we're trying to get the description of. -{1}: Thrown exception - - - Exceptions thrown: - Ausgelöste Ausnahmen: - This is usually precedes by TestAssembly_AssemblyDiscoveryFailure message, and preceded by list of exceptions thrown in a test discovery session. - - - Getting custom attributes for type {0} threw exception (will ignore and use the reflection way): {1} - Beim Abrufen von benutzerdefinierten Attributen für den Typ {0} wurde Ausnahme ausgelöst (wird ignoriert und die Reflektionsart verwendet): {1} - {0}: Attribute full type name. -{1}: Exception description - - - An older version of MSTestV2 package is loaded in assembly, test discovery might fail to discover all data tests if they depend on `.runsettings` file. - Eine ältere Version des MSTestV2-Pakets wird in die Assembly geladen. Bei der Testermittlung werden möglicherweise nicht alle Datentests ermittelt, wenn sie von der Datei ".runsettings" abhängen. - - - - The called code threw an exception that was caught, but the exception value was null - Der aufgerufene Code hat eine Ausnahme ausgelöst, die abgefangen wurde, aber der Ausnahmewert war NULL. - - - - Exception occurred while expanding IDataSource rows from attribute on "{0}.{1}": {2} - Ausnahme beim Erweitern von IDataSource-Zeilen aus dem Attribut auf "{0}.{1}": {2} - {0}: TypeName with namespace, -{1}: Method name, -{2}: CannotExpandIDataSourceAttribute_DuplicateDisplayName or CannotExpandIDataSourceAttribute_CannotSerialize - - - Display name "{2}" on indexes {0} and {1} are duplicate. Display names should be unique. - Der Anzeigename "{2}" für Indizes {0} und {1} ist doppelt vorhanden. Anzeigenamen sollten eindeutig sein. - {0}, {1}: Zero based index if an element inside of an array -{2}: Test display name. - - - Data on index {0} for "{1}" cannot be serialized. All data provided through "IDataSource" should be serializable. If you need to test non-serializable data sources, please make sure you add "TestDataSourceDiscovery" attribute on your test assembly and set the discovery option to "DuringExecution". - Daten im Index {0} für "{1}" können nicht serialisiert werden. Alle über "IDataSource" bereitgestellten Daten sollten serialisierbar sein. Wenn Sie nicht serialisierbare Datenquellen testen müssen, stellen Sie sicher, dass Sie der Testassembly das Attribut "TestDataSourceDiscovery" hinzufügen und die Ermittlungsoption auf "DuringExecution" festlegen. - {0}: Zero based index if an element inside of an array, -{1}: Test name - - - - \ No newline at end of file diff --git a/src/Adapter/MSTest.TestAdapter/Resources/xlf/Resource.es.xlf b/src/Adapter/MSTest.TestAdapter/Resources/xlf/Resource.es.xlf deleted file mode 100644 index b629fb9322..0000000000 --- a/src/Adapter/MSTest.TestAdapter/Resources/xlf/Resource.es.xlf +++ /dev/null @@ -1,482 +0,0 @@ - - - - - - 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 - - - - Assembly cleanup method '{0}.{1}' was canceled - Método de limpieza de ensamblado "{0}.{1}" se canceló - - - - 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 - - - - Assembly initialize method '{0}.{1}' was canceled - Método de inicialización de ensamblado "{0}.{1}" se canceló - - - - Cannot run test method '{0}.{1}': Test data doesn't match method parameters. Either the count or types are different. -Test expected {2} parameter(s), with types '{3}', -but received {4} argument(s), with types '{5}'. - No se puede ejecutar el método de prueba "{0}.{1}": los datos de prueba no coinciden con los parámetros del método. El recuento o los tipos son diferentes. -La prueba esperaba recibir {2} parámetro(s), con los tipos "{3}", -pero recibió {4} argumento(s), con los tipos "{5}". - - - - Cannot run test method '{0}.{1}': Method has parameters, but does not define any test source. Use '[DataRow]', '[DynamicData]', or a custom 'ITestDataSource' data source to provide test data. - No se puede ejecutar el método de prueba "{0}.{1}": el método tiene parámetros, pero no define ningún origen de prueba. Use "[DataRow]", "[DynamicData]" o un origen de datos "ITestDataSource" personalizado para proporcionar datos de prueba. - - - - 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 - - - - Class cleanup method '{0}.{1}' was canceled - Método de limpieza de clases "{0}.{1}" se canceló - - - - 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 - - - - Class initialize method '{0}.{1}' was canceled - Método de inicialización de clase "{0}.{1}" se canceló - - - - Both '.runsettings' and '.testconfig.json' files have been detected. Please select only one of these test configuration files. - Se han detectado los archivos ".runsettings" y ".testconfig.json". Seleccione solo uno de estos archivos de configuración de prueba. - - - - Test '{0}' timed out after {1}ms - Se agotó el tiempo de espera de la '{0}' de pruebas después de {1}ms - - - - The type of the generic parameter '{0}' could not be inferred. - No se pudo inferir el tipo del parámetro genérico '{0}'. - - - - The generic test method '{0}' doesn't have arguments, so the generic parameter cannot be inferred. - El método de prueba genérico '{0}' no tiene argumentos, por lo que no se puede inferir el parámetro genérico. - - - - Found two conflicting types for generic parameter '{0}'. The conflicting types are '{1}' and '{2}'. - Se encontraron dos tipos en conflicto para el parámetro genérico '{0}'. Los tipos en conflicto son '{1}' y '{2}'. - - - - Invalid value '{0}' for runsettings entry '{1}', setting will be ignored. - Valor ''{0}'' no válido para la entrada runsettings ''{1}'', se omitirá la configuración. - - - - Runsettings entry '<ExecutionApartmentState>STA</ExecutionApartmentState>' is not supported on non-Windows OSes. - La entrada runsettings "<ExecutionApartmentState>STA</ExecutionApartmentState>" no se admite en sistemas operativos que no son de Windows. - - - - Running tests in any of the provided sources is not supported for the selected platform - La ejecución de pruebas en las fuentes proporcionadas no se admite para la plataforma seleccionada - - - - 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 - - - - Test cleanup method '{0}.{1}' was canceled - Método de limpieza de pruebas "{0}.{1}" se canceló - - - - 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 - - - - Test initialize method '{0}.{1}' was canceled - Método de inicialización de prueba "{0}.{1}" se canceló - - - - TestCleanup method {0}.{1} threw exception. {2}. - El método TestCleanup {0}.{1} devolvió una excepción. {2}. - - - - --- End of inner exception stack trace --- - --- Fin del seguimiento de la pila de excepción interna --- - - - - UTA014: {0}: Cannot define more than one method with the AssemblyCleanup attribute inside an assembly. - UTA014: {0}: No se puede definir más de un método con el atributo AssemblyCleanup dentro de un ensamblado. - - - - UTA013: {0}: Cannot define more than one method with the AssemblyInitialize attribute inside an assembly. - UTA013: {0}: No se puede definir más de un método con el atributo AssemblyInitialize dentro de un ensamblado. - - - - UTA026: {0}: Cannot define more than one method with the ClassCleanup attribute inside a class. - UTA026: {0}: No se puede definir más de un método con el atributo ClassCleanup dentro de una clase. - - - - UTA025: {0}: Cannot define more than one method with the ClassInitialize attribute inside a class. - UTA025: {0}: No se puede definir más de un método con el atributo ClassInitialize dentro de una clase. - - - - UTA024: {0}: Cannot define more than one method with the TestCleanup attribute. - UTA024: {0}: No se puede definir más de un método con el atributo TestCleanup. - - - - UTA018: {0}: Cannot define more than one method with the TestInitialize attribute. - UTA018: {0}: No se puede definir más de un método con el atributo TestInitialize. - - - - TestClass attribute defined on generic non-abstract class {0} - Atributo TestClass definido en una clase genérica no abstracta {0} - - - - Initialization method {0}.{1} threw exception. {2}. - El método de inicialización {0}.{1} devolvió una excepción. {2}. - - - - Unable to create instance of class {0}. Error: {1}. - No se puede crear una instancia de la clase {0}. Error: {1}. - - - - The test method '{0}.{1}' has multiple attributes derived from '{2}' defined on it. Only one such attribute is allowed. - El método de prueba '{0}.{1}' tiene varios atributos derivados de '{2}' definidos en él. Solo se permite un atributo de este tipo. - - - - Cannot find a valid constructor for test class '{0}'. Valid constructors are 'public' and either parameterless or with one parameter of type 'TestContext'. - No se encuentra un constructor válido para la clase de prueba '{0}'. Los constructores válidos son 'public' y sin parámetros o con un parámetro de tipo 'TestContext'. - - - - Unable to set TestContext property for the class {0}. Error: {1}. - No se puede establecer la propiedad TestContext para la clase {0}. Error: {1}. - - - - (Failed to get the message for an exception of type {0} due to an exception.) - (No se pudo obtener el mensaje para una excepción del tipo {0} debido a una excepción.) - - - - UTA031: class {0} does not have valid TestContext property. TestContext must be of type TestContext, must be non-static, and must be public. For example: public TestContext TestContext. - UTA031: la clase {0}no tiene la propiedad TestContext válida. TestContext debe ser de tipo TestContext, debe ser no estática y debe ser pública. Por ejemplo: public TestContext TestContext. - - - - UTA001: TestClass attribute defined on non-public class {0} - UTA001: se ha definido el atributo TestClass en la clase no pública {0} - - - - MSTestAdapter failed to discover tests in class '{0}' of assembly '{1}' because {2}. - MSTestAdapter no detectó pruebas en la clase '{0}' del ensamblado '{1}' porque {2}. - - - - {0}: {1} - {0}: {1} - - - - Unable to load types from the test source '{0}'. Some or all of the tests in this source may not be discovered. -Error: {1} - No se pueden cargar tipos del origen de prueba "{0}". Puede que no se detecten algunas o ninguna de las pruebas de este origen. -Error: {1} - - - - File does not exist: {0} - El archivo no existe: {0} - - - - UTA007: Method {1} defined in class {0} does not have correct signature. Test method marked with the [TestMethod] attribute must be non-static, public, return-type as void and should not take any parameter. Example: public void Test.Class1.Test(). Additionally, if you are using async-await in test method then return-type must be 'Task' or 'ValueTask'. Example: public async Task Test.Class1.Test2() - UTA007: El método {1} definido en la clase {0} no tiene la firma correcta. El método de prueba marcado con el atributo [TestMethod] debe ser no estático, público, con el tipo devuelto void y no debe tomar ningún parámetro. Ejemplo: public void Test.Class1.Test(). Además, si está usando async-await en el método de prueba, entonces el tipo de valor devuelto debe ser 'Task' o 'ValueTask'. Ejemplo: public async Task Test.Class1.Test2() - - - - TestContext cannot be Null. - TestContext no será null. - - - - Assembly Cleanup method {0}.{1} failed. Error Message: {2}. StackTrace: {3} - Error de método Cleanup de ensamblado {0}.{1}. Mensaje de error: {2}. StackTrace: {3} - - - - Assembly Initialization method {0}.{1} threw exception. {2}: {3}. Aborting test execution. - Excepción método inicialización ensamblado {0}.{1}. {2}: {3}. Anulada ejecución de prueba. - - - - Class Cleanup method {0}.{1} failed. Error Message: {2}. Stack Trace: {3} - Error de método Cleanup de clase {0}.{1}. Mensaje error: {2}. Seguimiento de pila: {3} - - - - Class Initialization method {0}.{1} threw exception. {2}: {3}. - Excepción del método inicialización clase {0}. {1}. {2}: {3}. - - - - An unhandled exception was thrown by the 'Execute' method. Please report this error to the author of the attribute '{0}'. -{1} - El método 'Execute' produjo una excepción no controlada. Informe de este error al autor del atributo '{0}'. -{1} - - - - Error in executing test. No result returned by extension. If using extension of TestMethodAttribute then please contact vendor. - No se pudo ejecutar prueba. Extensión no devolvió resultados. Si usa extensión TestMethodAttribute, contacte con el proveedor. - - - - Method {0}.{1} has wrong signature. The method must be static, public, does not return a value and should not take any parameter. Additionally, if you are using async-await in method then return-type must be 'Task' or 'ValueTask'. - El método {0}.{1} tiene una firma incorrecta. El método debe ser estático, público, no devolver un valor y no aceptar ningún parámetro. Además, si está usando async-await en el método entonces el tipo de valor devuelto debe ser 'Task' o 'ValueTask'. - - - - Method {0}.{1} has wrong signature. The method must be static, public, does not return a value and should take a single parameter of type TestContext. Additionally, if you are using async-await in method then return-type must be 'Task' or 'ValueTask'. - El método {0}.{1} tiene una firma incorrecta. El método debe ser estático, público, no devolver ningún valor y debe tomar un único parámetro de tipo TestContext. Además, si está usando async-await en el método entonces el tipo de valor devuelto debe ser 'Task' o 'ValueTask'. - - - - UTA054: {0}.{1} has invalid Timeout attribute. The timeout must be an integer value greater than 0. - UTA054: {0}.{1} tiene un atributo Timeout no válido. El tiempo de espera debe ser un valor entero mayor que 0. - - - - UTA023: {0}: Cannot define predefined property {2} on method {1}. - UTA023: {0}: no se puede definir la propiedad predefinida {2} en el método {1}. - - - - UTA021: {0}: Null or empty custom property defined on method {1}. The custom property must have a valid name. - UTA021: {0}: se ha definido una propiedad personalizada nula o vacía en el método {1}. La propiedad personalizada debe tener un nombre válido. - - - - Method {0}.{1} does not exist. - El método {0}.{1} no existe. - - - - Unable to find property {0}.TestContext. Error:{1}. - No se puede encontrar la propiedad {0}.TestContext. Error:{1}. - - - - The {0}.TestContext has incorrect type. - Tipo {0}.TestContext no es correcto. - - - - Method {0}.{1} has wrong signature. The method must be non-static, public, does not return a value and should not take any parameter. Additionally, if you are using async-await in method then return-type must be 'Task' or 'ValueTask'. - El método {0}.{1} tiene una firma incorrecta. Debe ser un método no estático, público, no devolver ningún valor y no debe aceptar parámetros. Además, si está usando async-await en el método entonces el tipo de valor devuelto debe ser 'Task' o 'ValueTask'. - - - - Unable to get type {0}. Error: {1}. - No se puede obtener el tipo {0}. Error: {1}. - - - - Test method {0} was not found. - No encontrado método prueba {0}. - - - - Debug Trace: - Seguimiento de depuración: - - - - Failed to obtain the exception thrown by test method {0}.{1}. - No se pudo obtener la excepción iniciada por el método de prueba {0}.{1}. - - - - Test method {0}.{1} threw exception: -{2} - Excepción método de prueba {0}.{1}: -{2} - - - - {0} For UWP projects, if you are using UI objects in test consider using [UITestMethod] attribute instead of [TestMethod] to execute test in UI thread. - {0}. En proyectos de UWP, si usa objetos de interfaz de usuario en la prueba, podría usar el atributo [UITestMethod] en lugar de [TestMethod] para ejecutar la prueba en el subproceso de interfaz de usuario. - - - - MSTestAdapterV2 - MSTestAdapterV2 - - - - Invalid settings '{0}'. Unexpected XmlAttribute: '{1}'. - Valor no válido '{0}'. XmlAttribute no esperado: '{1}'. - - - - Invalid settings '{0}'. Unexpected XmlElement: '{1}'. - Valor no válido '{0}'. XmlElement no esperado: '{1}'. - - - - {0} (Data Row {1}) - {0} (Fila de datos {1}) - - - - The ExpectedException attribute defined on test method {0}.{1} threw an exception during construction. -{2} - El atributo ExpectedException definido en el método de prueba {0}.{1} inició una excepción durante la construcción. -{2} - - - - Warning : A testsettings file or a vsmdi file is not supported with the MSTest V2 Adapter. - Advertencia: No se admite un archivo testsettings o vsmdi con el adaptador de MSTest V2. - - - - TestContext Messages: - Mensajes de TestContext: - - - - Error calling Test Cleanup method for test class {0}: {1} - Error al llamar al método Test Cleanup para la clase de prueba {0}: {1} - - - - TestCleanup Stack Trace - Seguimiento de pila de TestCleanup - - - - [MSTest][Discovery][{0}] {1} - [MSTest][Discovery][{0}] {1} - - - - Test Parallelization enabled for {0} (Workers: {1}, Scope: {2}) - Probar paralelización habilitada para {0} (Trabajos: {1}, Ámbito: {2}) - `Workers` is a setting name that shouldn't be localized. 'Scope' is a setting name that shouldn't be localized. - - - Invalid value '{0}' specified for 'Scope'. Supported scopes are {1}. - Valor no válido "{0}" especificado para "Ámbito". Los ámbitos admitidos son {1}. - 'Scope' is a setting name that shouldn't be localized. - - - Invalid value '{0}' specified for 'Workers'. The value should be a non-negative integer. - Valor no válido "{0}" especificado para "Trabajadores". El valor debe ser un entero no negativo. - `Workers` is a setting name that shouldn't be localized. - - - Failed to discover tests from assembly {0}. Reason:{1} - No se pudieron detectar pruebas desde el ensamblado {0}. Motivo:{1} - - - - Test '{0}' was canceled - Se canceló la '{0}' de pruebas - - - - Invalid value '{0}' specified for 'ClassCleanupLifecycle'. Supported scopes are {1}. - Valor no válido "{0}" especificado para "ClassCleanupLifecycle". Los ámbitos admitidos son {1}. - 'ClassCleanupLifecycle' is a setting name that shouldn't be localized. - - - Exception occurred while enumerating IDataSource attribute on "{0}.{1}": {2} - Excepción al enumerar el atributo IDataSource en "{0}.{1}": {2} - {0}: TypeName with namespace, -{1}: Method name, -{2}: Exception details - - - "{0}": (Failed to get exception description due to an exception of type "{1}". - "{0}": (No se pudo obtener la descripción de la excepción debido a una excepción de tipo "{1}". - {0}: Type of the original exception that we're trying to get the description of. -{1}: Thrown exception - - - Exceptions thrown: - Excepciones devueltas: - This is usually precedes by TestAssembly_AssemblyDiscoveryFailure message, and preceded by list of exceptions thrown in a test discovery session. - - - Getting custom attributes for type {0} threw exception (will ignore and use the reflection way): {1} - Al obtener atributos personalizados para el tipo {0} se produjo una excepción (se omitirá y se usará la forma de reflexión): {1} - {0}: Attribute full type name. -{1}: Exception description - - - An older version of MSTestV2 package is loaded in assembly, test discovery might fail to discover all data tests if they depend on `.runsettings` file. - Hay una versión anterior del paquete MSTestV2 cargada en el ensamblado. Es posible que la detección de pruebas no detecte todas las pruebas de datos si dependen del archivo ".runsettings". - - - - The called code threw an exception that was caught, but the exception value was null - El código llamado produjo una excepción que se detectó, pero el valor de la excepción era null - - - - Exception occurred while expanding IDataSource rows from attribute on "{0}.{1}": {2} - Se produjo una excepción al expandir las filas de IDataSource del atributo en "{0}.{1}": {2} - {0}: TypeName with namespace, -{1}: Method name, -{2}: CannotExpandIDataSourceAttribute_DuplicateDisplayName or CannotExpandIDataSourceAttribute_CannotSerialize - - - Display name "{2}" on indexes {0} and {1} are duplicate. Display names should be unique. - El nombre para mostrar "{2}" en los índices {0} y {1} están duplicados. Los nombres para mostrar deben ser únicos. - {0}, {1}: Zero based index if an element inside of an array -{2}: Test display name. - - - Data on index {0} for "{1}" cannot be serialized. All data provided through "IDataSource" should be serializable. If you need to test non-serializable data sources, please make sure you add "TestDataSourceDiscovery" attribute on your test assembly and set the discovery option to "DuringExecution". - No se pueden serializar los datos del {0} de índice para "{1}". Todos los datos proporcionados a través de "IDataSource" deben ser serializables. Si necesita probar orígenes de datos no serializables, asegúrese de agregar el atributo "TestDataSourceDiscovery" en el ensamblado de prueba y establezca la opción de detección en "DuringExecution". - {0}: Zero based index if an element inside of an array, -{1}: Test name - - - - \ No newline at end of file diff --git a/src/Adapter/MSTest.TestAdapter/Resources/xlf/Resource.fr.xlf b/src/Adapter/MSTest.TestAdapter/Resources/xlf/Resource.fr.xlf deleted file mode 100644 index 945bc54b28..0000000000 --- a/src/Adapter/MSTest.TestAdapter/Resources/xlf/Resource.fr.xlf +++ /dev/null @@ -1,482 +0,0 @@ - - - - - - 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 - - - - Assembly cleanup method '{0}.{1}' was canceled - La méthode de nettoyage de l’assembly « {0}.{1} » a été annulée - - - - 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 - - - - Assembly initialize method '{0}.{1}' was canceled - La méthode d’initialisation de l’assembly « {0}.{1} » a été annulée - - - - Cannot run test method '{0}.{1}': Test data doesn't match method parameters. Either the count or types are different. -Test expected {2} parameter(s), with types '{3}', -but received {4} argument(s), with types '{5}'. - Impossible d’exécuter la méthode de test « {0}.{1} » : les données de test ne correspondent pas aux paramètres de la méthode. Le nombre ou les types sont différents. -Tester le ou les paramètres de {2} attendus, avec les types « {3} », -mais a reçu {4} argument(s), avec les types « {5} ». - - - - Cannot run test method '{0}.{1}': Method has parameters, but does not define any test source. Use '[DataRow]', '[DynamicData]', or a custom 'ITestDataSource' data source to provide test data. - Impossible d’exécuter la méthode de test « {0}.{1} » : la méthode a des paramètres, mais ne définit aucune source de test. Utilisez « [DataRow] », « [DynamicData] » ou une source de données « ITestDataSource » personnalisée pour fournir des données de test. - - - - 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 - - - - Class cleanup method '{0}.{1}' was canceled - La méthode de nettoyage de la classe « {0}.{1} » a été annulée - - - - 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 - - - - Class initialize method '{0}.{1}' was canceled - La méthode d’initialisation de la classe « {0}.{1} » a été annulée - - - - Both '.runsettings' and '.testconfig.json' files have been detected. Please select only one of these test configuration files. - Les fichiers « .runsettings » et « .testconfig.json » ont été détectés. Veuillez sélectionner un seul de ces fichiers de configuration de test. - - - - Test '{0}' timed out after {1}ms - Délai de '{0}' de test dépassé après {1}ms - - - - The type of the generic parameter '{0}' could not be inferred. - Impossible de déduire le type du paramètre générique '{0}'. - - - - The generic test method '{0}' doesn't have arguments, so the generic parameter cannot be inferred. - La méthode de test générique '{0}' n’a pas d’arguments. Le paramètre générique ne peut donc pas être déduit. - - - - Found two conflicting types for generic parameter '{0}'. The conflicting types are '{1}' and '{2}'. - Deux types en conflit ont été trouvés pour le paramètre générique '{0}'. Les types en conflit sont '{1}' et '{2}'. - - - - Invalid value '{0}' for runsettings entry '{1}', setting will be ignored. - Valeur non valide '{0}' pour l’entrée runsettings '{1}', le paramètre sera ignoré. - - - - Runsettings entry '<ExecutionApartmentState>STA</ExecutionApartmentState>' is not supported on non-Windows OSes. - L’entrée Runsettings « <ExecutionApartmentState>STA</ExecutionApartmentState> » n’est pas prise en charge sur les systèmes d’exploitation non Windows. - - - - Running tests in any of the provided sources is not supported for the selected platform - L'exécution de tests dans l'une des sources fournies n'est pas prise en charge pour la plateforme sélectionnée - - - - 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 - - - - Test cleanup method '{0}.{1}' was canceled - La méthode de nettoyage du test « {0}.{1} » a été annulée - - - - 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 - - - - Test initialize method '{0}.{1}' was canceled - La méthode d’initialisation du test « {0}.{1} » a été annulée - - - - TestCleanup method {0}.{1} threw exception. {2}. - La méthode TestCleanup {0}.{1} a levé une exception. {2}. - - - - --- End of inner exception stack trace --- - --- Fin de la trace de la pile d'exception interne --- - - - - UTA014: {0}: Cannot define more than one method with the AssemblyCleanup attribute inside an assembly. - UTA014 : {0} : impossible de définir plus d'une méthode avec l'attribut AssemblyCleanup à l'intérieur d'un assembly. - - - - UTA013: {0}: Cannot define more than one method with the AssemblyInitialize attribute inside an assembly. - UTA013 : {0} : impossible de définir plus d'une méthode avec l'attribut AssemblyInitialize à l'intérieur d'un assembly. - - - - UTA026: {0}: Cannot define more than one method with the ClassCleanup attribute inside a class. - UTA026 : {0} : impossible de définir plus d'une méthode avec l'attribut ClassCleanup à l'intérieur d'une classe. - - - - UTA025: {0}: Cannot define more than one method with the ClassInitialize attribute inside a class. - UTA025 : {0} : impossible de définir plus d'une méthode avec l'attribut ClassInitialize à l'intérieur d'une classe. - - - - UTA024: {0}: Cannot define more than one method with the TestCleanup attribute. - UTA024 : {0} : impossible de définir plus d'une méthode avec l'attribut TestCleanup. - - - - UTA018: {0}: Cannot define more than one method with the TestInitialize attribute. - UTA018 : {0} : impossible de définir plus d'une méthode avec l'attribut TestInitialize. - - - - TestClass attribute defined on generic non-abstract class {0} - Attribut TestClass défini sur une classe non abstraite générique {0} - - - - Initialization method {0}.{1} threw exception. {2}. - La méthode Initialization {0}.{1} a levé une exception. {2}. - - - - Unable to create instance of class {0}. Error: {1}. - Impossible de créer une instance de la classe {0}. Erreur : {1}. - - - - The test method '{0}.{1}' has multiple attributes derived from '{2}' defined on it. Only one such attribute is allowed. - La méthode de test « {0}.{1} » possède plusieurs attributs dérivés de « {2} » qui lui sont définis. Un seul attribut de ce type est autorisé. - - - - Cannot find a valid constructor for test class '{0}'. Valid constructors are 'public' and either parameterless or with one parameter of type 'TestContext'. - Impossible de trouver un constructeur valide pour la classe de test « {0} ». Les constructeurs valides sont « publics » et sans paramètre ou avec un paramètre de type « TestContext ». - - - - Unable to set TestContext property for the class {0}. Error: {1}. - Impossible de définir la propriété TestContext pour la classe {0}. Erreur : {1}. - - - - (Failed to get the message for an exception of type {0} due to an exception.) - (Échec de la réception du message pour une exception de type {0} en raison d'une exception.) - - - - UTA031: class {0} does not have valid TestContext property. TestContext must be of type TestContext, must be non-static, and must be public. For example: public TestContext TestContext. - UTA031 : la classe {0} n'a pas de propriété TestContext valide. TestContext doit être de type TestContext, doit être non statique et doit être public. Par exemple : public TestContext TestContext. - - - - UTA001: TestClass attribute defined on non-public class {0} - UTA001 : attribut TestClass défini sur la classe non publique {0} - - - - MSTestAdapter failed to discover tests in class '{0}' of assembly '{1}' because {2}. - MSTestAdapter n'a pas découvert de tests dans la classe '{0}' de l'assembly '{1}', car {2}. - - - - {0}: {1} - {0} : {1} - - - - Unable to load types from the test source '{0}'. Some or all of the tests in this source may not be discovered. -Error: {1} - Impossible de charger les types à partir de la source de tests '{0}'. Une partie ou l'ensemble des tests de cette source ne peuvent pas être découverts. -Erreur : {1} - - - - File does not exist: {0} - Fichier inexistant : {0} - - - - UTA007: Method {1} defined in class {0} does not have correct signature. Test method marked with the [TestMethod] attribute must be non-static, public, return-type as void and should not take any parameter. Example: public void Test.Class1.Test(). Additionally, if you are using async-await in test method then return-type must be 'Task' or 'ValueTask'. Example: public async Task Test.Class1.Test2() - UTA007 : la méthode {1} définie dans la classe {0} ne dispose pas d'une signature correcte. Une méthode de test marquée avec l'attribut [TestMethod] doit être non statique, doit utiliser void pour return-type et ne doit accepter aucun paramètre. Exemple : public void Test.Class1.Test(). En outre, si vous utilisez async-await dans la méthode test, return-type doit être « Task » ou « ValueTask ». Exemple : public async Task Test.Class1.Test2() - - - - TestContext cannot be Null. - TestContext ne peut pas être null. - - - - Assembly Cleanup method {0}.{1} failed. Error Message: {2}. StackTrace: {3} - La méthode Cleanup d'assembly {0}.{1} a échoué. Message d'erreur : {2}. StackTrace : {3} - - - - Assembly Initialization method {0}.{1} threw exception. {2}: {3}. Aborting test execution. - La méthode d'assembly Initialization {0}.{1} a levé une exception. {2} : {3}. Abandon de l'exécution de tests. - - - - Class Cleanup method {0}.{1} failed. Error Message: {2}. Stack Trace: {3} - La méthode de classe Cleanup {0}.{1} a échoué. Message d'erreur : {2}. Trace de la pile : {3} - - - - Class Initialization method {0}.{1} threw exception. {2}: {3}. - La méthode de classe Initialization {0}.{1} a levé une exception. {2} : {3}. - - - - An unhandled exception was thrown by the 'Execute' method. Please report this error to the author of the attribute '{0}'. -{1} - Une exception non gérée a été levée par la méthode 'Execute'. Signalez cette erreur à l’auteur de l’attribut '{0}'. -{1} - - - - Error in executing test. No result returned by extension. If using extension of TestMethodAttribute then please contact vendor. - Erreur lors de l'exécution du test. L'extension n'a retourné aucun résultat. Si vous utilisez l'extension de TestMethodAttribute, contactez le fournisseur. - - - - Method {0}.{1} has wrong signature. The method must be static, public, does not return a value and should not take any parameter. Additionally, if you are using async-await in method then return-type must be 'Task' or 'ValueTask'. - La méthode {0}.{1} présente une signature incorrecte. La méthode doit être statique, publique et ne doit retourner aucune valeur ni accepter aucun paramètre. En outre, si vous utilisez async-await dans la méthode, return-type doit être « Task » ou « ValueTask ». - - - - Method {0}.{1} has wrong signature. The method must be static, public, does not return a value and should take a single parameter of type TestContext. Additionally, if you are using async-await in method then return-type must be 'Task' or 'ValueTask'. - La méthode {0}.{1} présente une signature incorrecte. La méthode doit être statique, publique et ne doit retourner aucune valeur et accepter un seul paramètre de type TestContext. En outre, si vous utilisez async-await dans la méthode, return-type doit être « Task » ou « ValueTask ». - - - - UTA054: {0}.{1} has invalid Timeout attribute. The timeout must be an integer value greater than 0. - UTA054 : {0}.{1}possède un attribut de délai d’expiration non valide. Le délai d’expiration doit être un nombre entier supérieur à 0. - - - - UTA023: {0}: Cannot define predefined property {2} on method {1}. - UTA023 : {0} : Impossible de définir la propriété prédéfinie {2} sur la méthode {1}. - - - - UTA021: {0}: Null or empty custom property defined on method {1}. The custom property must have a valid name. - UTA021 : {0} : Une propriété null ou vide personnalisée est définie sur la méthode {1}. La propriété personnalisée doit posséder un nom valide. - - - - Method {0}.{1} does not exist. - La méthode {0}.{1} n'existe pas. - - - - Unable to find property {0}.TestContext. Error:{1}. - Propriété {0}.TestContext introuvable. Erreur :{1}. - - - - The {0}.TestContext has incorrect type. - {0}.TestContext possède un type incorrect. - - - - Method {0}.{1} has wrong signature. The method must be non-static, public, does not return a value and should not take any parameter. Additionally, if you are using async-await in method then return-type must be 'Task' or 'ValueTask'. - La méthode {0}.{1} présente une signature incorrecte. La méthode doit être non statique, publique et ne doit retourner aucune valeur ni accepter aucun paramètre. En outre, si vous utilisez async-await dans la méthode, return-type doit être « Task » ou « ValueTask ». - - - - Unable to get type {0}. Error: {1}. - Impossible d'obtenir le type {0}. Erreur : {1}. - - - - Test method {0} was not found. - Méthode de test {0} introuvable. - - - - Debug Trace: - Trace du débogage : - - - - Failed to obtain the exception thrown by test method {0}.{1}. - Échec de l'obtention de l'exception levée par la méthode de test {0}.{1}. - - - - Test method {0}.{1} threw exception: -{2} - La méthode de test {0}.{1} a levé une exception : -{2} - - - - {0} For UWP projects, if you are using UI objects in test consider using [UITestMethod] attribute instead of [TestMethod] to execute test in UI thread. - {0} Pour les projets UWP, si vous utilisez des objets d'IU dans un test, utilisez l'attribut [UITestMethod] à la place de [TestMethod] pour exécuter le test dans le thread d'interface utilisateur. - - - - MSTestAdapterV2 - MSTestAdapterV2 - - - - Invalid settings '{0}'. Unexpected XmlAttribute: '{1}'. - Paramètres non valides '{0}'. XmlAttribute inattendu : '{1}'. - - - - Invalid settings '{0}'. Unexpected XmlElement: '{1}'. - Paramètres non valides '{0}'. XmlElement inattendu : '{1}'. - - - - {0} (Data Row {1}) - {0} (ligne de données {1}) - - - - The ExpectedException attribute defined on test method {0}.{1} threw an exception during construction. -{2} - L'attribut ExpectedException défini dans la méthode de test {0}.{1} a levé une exception durant la construction. -{2} - - - - Warning : A testsettings file or a vsmdi file is not supported with the MSTest V2 Adapter. - Avertissement : L'adaptateur MSTest V2 ne prend pas en charge les fichiers testsettings ou vsmdi. - - - - TestContext Messages: - Messages TestContext : - - - - Error calling Test Cleanup method for test class {0}: {1} - Erreur lors de l'appel de la méthode Test Cleanup pour la classe de test {0} : {1} - - - - TestCleanup Stack Trace - Trace de la pile TestCleanup - - - - [MSTest][Discovery][{0}] {1} - [MSTest][Discovery][{0}] {1} - - - - Test Parallelization enabled for {0} (Workers: {1}, Scope: {2}) - Parallélisation des tests activée pour {0} (Workers : {1}, Étendue : {2}). - `Workers` is a setting name that shouldn't be localized. 'Scope' is a setting name that shouldn't be localized. - - - Invalid value '{0}' specified for 'Scope'. Supported scopes are {1}. - Valeur non valide '{0}' spécifiée pour la 'Portée'. Les portées prises en charge sont {1}. - 'Scope' is a setting name that shouldn't be localized. - - - Invalid value '{0}' specified for 'Workers'. The value should be a non-negative integer. - Valeur non valide '{0}' spécifiée pour 'Workers'. La valeur doit être un entier non négatif. - `Workers` is a setting name that shouldn't be localized. - - - Failed to discover tests from assembly {0}. Reason:{1} - Échec de la découverte de tests à partir de l'assembly {0}. Raison :{1} - - - - Test '{0}' was canceled - Le test '{0}' a été annulé - - - - Invalid value '{0}' specified for 'ClassCleanupLifecycle'. Supported scopes are {1}. - Valeur non valide '{0}' spécifiée pour la 'ClassCleanupLifecycle'. Les portées prises en charge sont {1}. - 'ClassCleanupLifecycle' is a setting name that shouldn't be localized. - - - Exception occurred while enumerating IDataSource attribute on "{0}.{1}": {2} - Une exception s’est produite lors de l’énumération de l’attribut IDataSource sur « {0}.{1} » : {2} - {0}: TypeName with namespace, -{1}: Method name, -{2}: Exception details - - - "{0}": (Failed to get exception description due to an exception of type "{1}". - « {0} » : (Échec de l’obtention de la description de l’exception en raison d’une exception de type « {1} ». - {0}: Type of the original exception that we're trying to get the description of. -{1}: Thrown exception - - - Exceptions thrown: - Exceptions levées/s : - This is usually precedes by TestAssembly_AssemblyDiscoveryFailure message, and preceded by list of exceptions thrown in a test discovery session. - - - Getting custom attributes for type {0} threw exception (will ignore and use the reflection way): {1} - L’obtention d’attributs personnalisés pour le type {0} a levé une exception (ignorera et utilisera la méthode de réflexion) : {1} - {0}: Attribute full type name. -{1}: Exception description - - - An older version of MSTestV2 package is loaded in assembly, test discovery might fail to discover all data tests if they depend on `.runsettings` file. - Une version antérieure du package MSTestV2 est chargée dans l’assembly. La découverte de tests risque de ne pas découvrir tous les tests de données s’ils dépendent du fichier '.runsettings'. - - - - The called code threw an exception that was caught, but the exception value was null - Le code appelé a levé une exception qui a été interceptée, mais la valeur de l’exception était nul - - - - Exception occurred while expanding IDataSource rows from attribute on "{0}.{1}": {2} - Une exception s’est produite lors du développement des lignes IDataSource à partir de l’attribut sur « {0}.{1} » : {2} - {0}: TypeName with namespace, -{1}: Method name, -{2}: CannotExpandIDataSourceAttribute_DuplicateDisplayName or CannotExpandIDataSourceAttribute_CannotSerialize - - - Display name "{2}" on indexes {0} and {1} are duplicate. Display names should be unique. - Le nom d’affichage « {2} » sur les index {0} et {1} est dupliqué. Les noms d’affichage doivent être uniques. - {0}, {1}: Zero based index if an element inside of an array -{2}: Test display name. - - - Data on index {0} for "{1}" cannot be serialized. All data provided through "IDataSource" should be serializable. If you need to test non-serializable data sources, please make sure you add "TestDataSourceDiscovery" attribute on your test assembly and set the discovery option to "DuringExecution". - Impossible de sérialiser les données de l’index {0} pour « {1} ». Toutes les données fournies via « IDataSource » doivent être sérialisables. Si vous devez tester des sources de données non sérialisables, veillez à ajouter l’attribut « TestDataSourceDiscovery » à votre assembly de test et définissez l’option de découverte sur « DuringExecution ». - {0}: Zero based index if an element inside of an array, -{1}: Test name - - - - \ No newline at end of file diff --git a/src/Adapter/MSTest.TestAdapter/Resources/xlf/Resource.it.xlf b/src/Adapter/MSTest.TestAdapter/Resources/xlf/Resource.it.xlf deleted file mode 100644 index 97bff51c03..0000000000 --- a/src/Adapter/MSTest.TestAdapter/Resources/xlf/Resource.it.xlf +++ /dev/null @@ -1,482 +0,0 @@ - - - - - - Assembly cleanup method '{0}.{1}' timed out after {2}ms - Metodo di pulizia dell’assembly '{0}. Time out di {1}' dopo {2} ms - - - - Assembly cleanup method '{0}.{1}' was canceled - Il metodo di pulizia dell'assembly "{0}.{1}" è stato annullato - - - - Assembly initialize method '{0}.{1}' timed out after {2}ms - Metodo di inizializzazione dell'assembly '{0}. Timeout di {1}' dopo {2} ms - - - - Assembly initialize method '{0}.{1}' was canceled - Il metodo di inizializzazione dell'assembly "{0}.{1}" è stato annullato - - - - Cannot run test method '{0}.{1}': Test data doesn't match method parameters. Either the count or types are different. -Test expected {2} parameter(s), with types '{3}', -but received {4} argument(s), with types '{5}'. - Impossibile eseguire il metodo di test "{0}.{1}": i dati del test non corrispondono ai parametri del metodo. Il numero o il tipo è diverso. -Il test prevedeva {2} parametri, con tipi "{3}", -ma ha ricevuto {4} argomenti, con tipi "{5}". - - - - Cannot run test method '{0}.{1}': Method has parameters, but does not define any test source. Use '[DataRow]', '[DynamicData]', or a custom 'ITestDataSource' data source to provide test data. - Impossibile eseguire il metodo di test "{0}.{1}": il metodo contiene parametri, ma non definisce alcuna origine test. Usare "[DataRow]", "[DynamicData]" o un'origine dati "ITestDataSource" personalizzata per fornire i dati del test. - - - - 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 - - - - Class cleanup method '{0}.{1}' was canceled - Il metodo di pulizia della classe "{0}.{1}" è stato annullato - - - - Class initialize method '{0}.{1}' timed out after {2}ms - Metodo di inizializzazione della classe '{0}. Timeout di {1}' dopo {2} ms - - - - Class initialize method '{0}.{1}' was canceled - Il metodo di inizializzazione della classe "{0}.{1}" è stato annullato - - - - Both '.runsettings' and '.testconfig.json' files have been detected. Please select only one of these test configuration files. - Sono stati rilevati sia i file '.runsettings' sia '.testconfig.json'. Selezionare solo uno di questi file di configurazione di test. - - - - Test '{0}' timed out after {1}ms - Timeout del '{0}' di test dopo {1}ms - - - - The type of the generic parameter '{0}' could not be inferred. - Impossibile dedurre il tipo del parametro generico '{0}'. - - - - The generic test method '{0}' doesn't have arguments, so the generic parameter cannot be inferred. - Il metodo di test generico '{0}' non contiene argomenti, di conseguenza non è possibile dedurre il parametro generico. - - - - Found two conflicting types for generic parameter '{0}'. The conflicting types are '{1}' and '{2}'. - Sono stati trovati due tipi in conflitto per il parametro generico '{0}'. I tipi in conflitto sono '{1}' e '{2}'. - - - - Invalid value '{0}' for runsettings entry '{1}', setting will be ignored. - Valore non valido '{0}' per la voce runsettings '{1}'. L'impostazione verrà ignorata. - - - - Runsettings entry '<ExecutionApartmentState>STA</ExecutionApartmentState>' is not supported on non-Windows OSes. - La voce Runsettings '<ExecutionApartmentState>STA</ExecutionApartmentState>' non è supportata nei sistemi operativi non Windows. - - - - Running tests in any of the provided sources is not supported for the selected platform - L'esecuzione di test in una delle origini specificate non è supportata per la piattaforma selezionata - - - - 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 - - - - Test cleanup method '{0}.{1}' was canceled - Il metodo di pulizia del test "{0}.{1}" è stato annullato - - - - Test initialize method '{0}.{1}' timed out after {2}ms - Metodo di inizializzazione del test '{0}. Timeout di {1}' dopo {2} ms - - - - Test initialize method '{0}.{1}' was canceled - Il metodo di inizializzazione del test "{0}.{1}" è stato annullato - - - - TestCleanup method {0}.{1} threw exception. {2}. - Il metodo TestCleanup {0}.{1} ha generato un'eccezione. {2}. - - - - --- End of inner exception stack trace --- - --- Fine dell'analisi dello stack dell'eccezione interna --- - - - - UTA014: {0}: Cannot define more than one method with the AssemblyCleanup attribute inside an assembly. - UTA014: {0}: non è possibile definire più di un metodo con l'attributo AssemblyCleanup all'interno di un assembly. - - - - UTA013: {0}: Cannot define more than one method with the AssemblyInitialize attribute inside an assembly. - UTA013: {0}: non è possibile definire più di un metodo con l'attributo AssemblyInitialize all'interno di un assembly. - - - - UTA026: {0}: Cannot define more than one method with the ClassCleanup attribute inside a class. - UTA026: {0}: non è possibile definire più di un metodo con l'attributo ClassCleanup all'interno di una classe. - - - - UTA025: {0}: Cannot define more than one method with the ClassInitialize attribute inside a class. - UTA025: {0}: non è possibile definire più di un metodo con l'attributo ClassInitialize all'interno di una classe. - - - - UTA024: {0}: Cannot define more than one method with the TestCleanup attribute. - UTA024: {0}: non è possibile definire più di un metodo con l'attributo TestCleanup. - - - - UTA018: {0}: Cannot define more than one method with the TestInitialize attribute. - UTA018: {0}: non è possibile definire più di un metodo con l'attributo TestInitialize. - - - - TestClass attribute defined on generic non-abstract class {0} - Attributo TestClass definito nella classe generica non astratta {0} - - - - Initialization method {0}.{1} threw exception. {2}. - Il metodo di inizializzazione {0}.{1} ha generato un'eccezione. {2}. - - - - Unable to create instance of class {0}. Error: {1}. - Non è possibile creare un'istanza della classe {0}. Errore: {1}. - - - - The test method '{0}.{1}' has multiple attributes derived from '{2}' defined on it. Only one such attribute is allowed. - Il metodo di test '{0}.{1}' contiene più attributi derivati da '{2}' definito in esso. È consentito solo uno di tali attributi. - - - - Cannot find a valid constructor for test class '{0}'. Valid constructors are 'public' and either parameterless or with one parameter of type 'TestContext'. - Impossibile trovare un costruttore valido per la classe di test '{0}'. I costruttori validi sono 'public' e senza parametri o con un parametro di tipo 'TestContext'. - - - - Unable to set TestContext property for the class {0}. Error: {1}. - Non è possibile impostare la proprietà TestContext per la classe {0}. Errore: {1}. - - - - (Failed to get the message for an exception of type {0} due to an exception.) - Non è stato possibile ottenere il messaggio per un'eccezione di tipo {0} a causa di un'eccezione. - - - - UTA031: class {0} does not have valid TestContext property. TestContext must be of type TestContext, must be non-static, and must be public. For example: public TestContext TestContext. - UTA031: la classe {0} non dispone di una proprietà TestContext valida. La proprietà TestContext deve essere di tipo TestContext, non deve essere statica e deve essere pubblica. Ad esempio: public TestContext TestContext. - - - - UTA001: TestClass attribute defined on non-public class {0} - UTA001: è stato definito l'attributo TestClass per la classe non pubblica {0} - - - - MSTestAdapter failed to discover tests in class '{0}' of assembly '{1}' because {2}. - MSTestAdapter non è riuscito a individuare test nella classe '{0}' dell'assembly '{1}' perché {2}. - - - - {0}: {1} - {0}: {1} - - - - Unable to load types from the test source '{0}'. Some or all of the tests in this source may not be discovered. -Error: {1} - Non è possibile caricare i tipi dall'origine test '{0}'. È possibile che alcuni o tutti i test non siano stati individuati in questa origine. -Errore: {1} - - - - File does not exist: {0} - Il file {0} non esiste - - - - UTA007: Method {1} defined in class {0} does not have correct signature. Test method marked with the [TestMethod] attribute must be non-static, public, return-type as void and should not take any parameter. Example: public void Test.Class1.Test(). Additionally, if you are using async-await in test method then return-type must be 'Task' or 'ValueTask'. Example: public async Task Test.Class1.Test2() - UTA007: la firma del metodo {1} definito nella classe {0} non è corretta. Il metodo di test contrassegnato con l'attributo [TestMethod] deve essere pubblico e non statico, non deve accettare parametri e deve includere un tipo restituito void. Esempio: public void Test.Class1.Test(). Se inoltre nel metodo di test si usa async-await, il tipo restituito deve essere 'Task' o 'ValueTask'. Esempio: public async Task Test.Class1.Test2() - - - - TestContext cannot be Null. - TestContext non può essere Null. - - - - Assembly Cleanup method {0}.{1} failed. Error Message: {2}. StackTrace: {3} - Il metodo di pulizia assembly {0}.{1} non è riuscito. Messaggio di errore: {2}. Analisi dello stack: {3} - - - - Assembly Initialization method {0}.{1} threw exception. {2}: {3}. Aborting test execution. - Il metodo di inizializzazione assembly {0}.{1} ha generato un'eccezione. {2}: {3}. L'esecuzione del test verrà interrotta. - - - - Class Cleanup method {0}.{1} failed. Error Message: {2}. Stack Trace: {3} - Il metodo di pulizia classi {0}.{1} non è riuscito. Messaggio di errore: {2}. Analisi dello stack: {3} - - - - Class Initialization method {0}.{1} threw exception. {2}: {3}. - Il metodo di inizializzazione classi {0}.{1} ha generato un'eccezione. {2}: {3}. - - - - An unhandled exception was thrown by the 'Execute' method. Please report this error to the author of the attribute '{0}'. -{1} - Eccezione non gestita generata dal metodo 'Execute'. Segnalare l'errore all'autore dell'attributo '{0}'. -{1} - - - - Error in executing test. No result returned by extension. If using extension of TestMethodAttribute then please contact vendor. - Si è verificato un errore durante l'esecuzione del test. L'estensione non ha restituito alcun risultato. Se si usa l'estensione di TestMethodAttribute, contattare il fornitore. - - - - Method {0}.{1} has wrong signature. The method must be static, public, does not return a value and should not take any parameter. Additionally, if you are using async-await in method then return-type must be 'Task' or 'ValueTask'. - La firma del metodo {0}.{1}non è corretta. Il metodo deve essere statico e pubblico, non deve restituire un valore né accettare parametri. Se inoltre si usa async-await nel metodo di test, il tipo restituito deve essere 'Task' o 'ValueTask'. - - - - Method {0}.{1} has wrong signature. The method must be static, public, does not return a value and should take a single parameter of type TestContext. Additionally, if you are using async-await in method then return-type must be 'Task' or 'ValueTask'. - La firma del metodo {0}.{1}non è corretta. Il metodo deve essere statico e pubblico, non deve restituire un valore e deve accettare un singolo parametro di tipo TestContext. Se inoltre si usa async-await nel metodo di test, il tipo restituito deve essere 'Task' o 'ValueTask'. - - - - UTA054: {0}.{1} has invalid Timeout attribute. The timeout must be an integer value greater than 0. - UTA054: {0}.{1} ha un attributo Timeout non valido. Il timeout deve essere un valore intero maggiore di 0. - - - - UTA023: {0}: Cannot define predefined property {2} on method {1}. - UTA023: {0}: non è possibile definire la proprietà predefinita {2} per il metodo {1}. - - - - UTA021: {0}: Null or empty custom property defined on method {1}. The custom property must have a valid name. - UTA021: {0}: per il metodo {1} è stata definita una proprietà personalizzata Null o vuota. Specificare un nome valido per la proprietà personalizzata. - - - - Method {0}.{1} does not exist. - Il metodo {0}.{1} non esiste. - - - - Unable to find property {0}.TestContext. Error:{1}. - La proprietà {0}.TestContext non è stata trovata. Errore: {1}. - - - - The {0}.TestContext has incorrect type. - Il tipo di {0}.TestContext non è corretto. - - - - Method {0}.{1} has wrong signature. The method must be non-static, public, does not return a value and should not take any parameter. Additionally, if you are using async-await in method then return-type must be 'Task' or 'ValueTask'. - La firma del metodo {0}.{1}non è corretta. Il metodo deve essere non statico e pubblico, non deve restituire un valore né accettare parametri. Se inoltre si usa async-await nel metodo di test, il tipo restituito deve essere 'Task' o 'ValueTask'. - - - - Unable to get type {0}. Error: {1}. - Non è possibile ottenere il tipo {0}. Errore: {1}. - - - - Test method {0} was not found. - Il metodo di test {0} non è stato trovato. - - - - Debug Trace: - Traccia di debug: - - - - Failed to obtain the exception thrown by test method {0}.{1}. - Non è stato possibile ottenere l'eccezione generata dal metodo di test {0}.{1}. - - - - Test method {0}.{1} threw exception: -{2} - Il metodo di test {0}.{1} ha generato un'eccezione: -{2} - - - - {0} For UWP projects, if you are using UI objects in test consider using [UITestMethod] attribute instead of [TestMethod] to execute test in UI thread. - {0}. Se, per i progetti della piattaforma UWP, nel test si usano oggetti dell'interfaccia utente, provare a specificare l'attributo [UITestMethod] invece di [TestMethod] per eseguire il test nel thread di UI. - - - - MSTestAdapterV2 - MSTestAdapterV2 - - - - Invalid settings '{0}'. Unexpected XmlAttribute: '{1}'. - Le impostazioni '{0}' non sono valide. Elemento XmlAttribute imprevisto: '{1}'. - - - - Invalid settings '{0}'. Unexpected XmlElement: '{1}'. - Le impostazioni '{0}' non sono valide. Elemento XmlElement imprevisto: '{1}'. - - - - {0} (Data Row {1}) - {0} (riga dati {1}) - - - - The ExpectedException attribute defined on test method {0}.{1} threw an exception during construction. -{2} - L'attributo ExpectedException definito nel metodo di test {0}.{1} ha generato un'eccezione durante la costruzione. -{2} - - - - Warning : A testsettings file or a vsmdi file is not supported with the MSTest V2 Adapter. - Avviso: con l'adattatore MSTest V2 non è possibile usare un file testsettings o un file vsmdi. - - - - TestContext Messages: - Messaggi di TestContext: - - - - Error calling Test Cleanup method for test class {0}: {1} - Si è verificato un errore durante la chiamata del metodo TestCleanup per la classe di test {0}: {1} - - - - TestCleanup Stack Trace - Analisi dello stato di TestCleanup - - - - [MSTest][Discovery][{0}] {1} - [MSTest][Individuazione][{0}] {1} - - - - Test Parallelization enabled for {0} (Workers: {1}, Scope: {2}) - Parallelizzazione test abilitata per {0} (Processi di lavoro: {1}. Ambito: {2}). - `Workers` is a setting name that shouldn't be localized. 'Scope' is a setting name that shouldn't be localized. - - - Invalid value '{0}' specified for 'Scope'. Supported scopes are {1}. - Il valore '{0}', specificato per 'Scope', non è valido. I valori supportati per Scope sono {1}. - 'Scope' is a setting name that shouldn't be localized. - - - Invalid value '{0}' specified for 'Workers'. The value should be a non-negative integer. - Il valore '{0}', specificato per 'Workers', non è valido. Il valore deve essere un numero intero non negativo. - `Workers` is a setting name that shouldn't be localized. - - - Failed to discover tests from assembly {0}. Reason:{1} - Non è stato possibile individuare i test dall'assembly {0}. Motivo: {1} - - - - Test '{0}' was canceled - Il '{0}' di test è stato annullato - - - - Invalid value '{0}' specified for 'ClassCleanupLifecycle'. Supported scopes are {1}. - Il valore '{0}', specificato per 'ClassCleanupLifecycle', non è valido. I valori supportati per Scope sono {1}. - 'ClassCleanupLifecycle' is a setting name that shouldn't be localized. - - - Exception occurred while enumerating IDataSource attribute on "{0}.{1}": {2} - Si è verificata un'eccezione durante l'enumerazione dell'attributo IDataSource in "{0}.{1}": {2} - {0}: TypeName with namespace, -{1}: Method name, -{2}: Exception details - - - "{0}": (Failed to get exception description due to an exception of type "{1}". - "{0}": (non è stato possibile ottenere la descrizione dell'eccezione a causa di un'eccezione di tipo "{1}". - {0}: Type of the original exception that we're trying to get the description of. -{1}: Thrown exception - - - Exceptions thrown: - Eccezioni generate: - This is usually precedes by TestAssembly_AssemblyDiscoveryFailure message, and preceded by list of exceptions thrown in a test discovery session. - - - Getting custom attributes for type {0} threw exception (will ignore and use the reflection way): {1} - Il recupero degli attributi personalizzati per il tipo {0} ha generato un'eccezione (verrà ignorata e verrà usata la modalità reflection): {1} - {0}: Attribute full type name. -{1}: Exception description - - - An older version of MSTestV2 package is loaded in assembly, test discovery might fail to discover all data tests if they depend on `.runsettings` file. - Nell'assembly è caricata una versione precedente del pacchetto MSTestV2. L'individuazione dei test potrebbe non riuscire a individuare tutti i test dei dati se dipendono dal file '.runsettings'. - - - - The called code threw an exception that was caught, but the exception value was null - Il codice chiamato ha generato un'eccezione che è stata rilevata, ma il valore dell'eccezione è Null - - - - Exception occurred while expanding IDataSource rows from attribute on "{0}.{1}": {2} - Si è verificata un'eccezione durante l'espansione delle righe IDataSource dall'attributo in "{0}.{1}": {2} - {0}: TypeName with namespace, -{1}: Method name, -{2}: CannotExpandIDataSourceAttribute_DuplicateDisplayName or CannotExpandIDataSourceAttribute_CannotSerialize - - - Display name "{2}" on indexes {0} and {1} are duplicate. Display names should be unique. - Il nome visualizzato "{2}" negli indici {0} e {1} è duplicato. I nomi visualizzati devono essere univoci. - {0}, {1}: Zero based index if an element inside of an array -{2}: Test display name. - - - Data on index {0} for "{1}" cannot be serialized. All data provided through "IDataSource" should be serializable. If you need to test non-serializable data sources, please make sure you add "TestDataSourceDiscovery" attribute on your test assembly and set the discovery option to "DuringExecution". - Non è possibile serializzare i dati nell'indice {0} per "{1}". Tutti i dati forniti tramite "IDataSource" devono essere serializzabili. Se è necessario testare origini dati non serializzabili, assicurarsi di aggiungere l'attributo "TestDataSourceDiscovery" nell'assembly di test e impostare l'opzione di individuazione su "DuringExecution". - {0}: Zero based index if an element inside of an array, -{1}: Test name - - - - \ No newline at end of file diff --git a/src/Adapter/MSTest.TestAdapter/Resources/xlf/Resource.ja.xlf b/src/Adapter/MSTest.TestAdapter/Resources/xlf/Resource.ja.xlf deleted file mode 100644 index 0e196b3b3f..0000000000 --- a/src/Adapter/MSTest.TestAdapter/Resources/xlf/Resource.ja.xlf +++ /dev/null @@ -1,483 +0,0 @@ - - - - - - Assembly cleanup method '{0}.{1}' timed out after {2}ms - アセンブリ クリーンアップ メソッド '{0}.{1}' が {2}ms 後にタイムアウトしました - - - - Assembly cleanup method '{0}.{1}' was canceled - アセンブリ クリーンアップ メソッド '{0}.{1}' が取り消されました - - - - Assembly initialize method '{0}.{1}' timed out after {2}ms - アセンブリ初期化メソッド '{0}.{1}' が {2}ms 後にタイムアウトになりました - - - - Assembly initialize method '{0}.{1}' was canceled - アセンブリ初期化メソッド '{0}.{1}' が取り消されました - - - - Cannot run test method '{0}.{1}': Test data doesn't match method parameters. Either the count or types are different. -Test expected {2} parameter(s), with types '{3}', -but received {4} argument(s), with types '{5}'. - テスト メソッド '{0}を実行できません。{1}': テスト データがメソッド パラメーターと一致しません。カウントまたは型が異なります。 -型 '{3}'、 - を持つ、予期された {2} パラメーターをテストします -ただし、型 '{5}' の引数 {4} を受け取りました。 - - - - Cannot run test method '{0}.{1}': Method has parameters, but does not define any test source. Use '[DataRow]', '[DynamicData]', or a custom 'ITestDataSource' data source to provide test data. - テスト メソッド '{0} を実行できません。{1}': メソッドにはパラメーターがありますが、テスト ソースは定義されていません。'[DataRow]'、'[DynamicData]'、カスタムの 'ITestDataSource' データ ソースを使用して、テスト データを提供します。 - - - - Class cleanup method '{0}.{1}' timed out after {2}ms - クラス クリーンアップ メソッド '{0}.{1}' が {2}ms 後にタイムアウトしました - - - - Class cleanup method '{0}.{1}' was canceled - クラス クリーンアップ メソッド '{0}.{1}' が取り消されました - - - - Class initialize method '{0}.{1}' timed out after {2}ms - クラス初期化メソッド '{0}.{1}' が {2}ms 後にタイムアウトになりました - - - - Class initialize method '{0}.{1}' was canceled - クラス初期化メソッド '{0}.{1}' が取り消されました - - - - Both '.runsettings' and '.testconfig.json' files have been detected. Please select only one of these test configuration files. - '.runsettings' ファイルと '.testconfig.json' ファイルの両方が検出されました。これらのテスト構成ファイルのいずれか 1 つだけを選択してください。 - - - - Test '{0}' timed out after {1}ms - テスト '{0}' が {1}ミリ秒後にタイムアウトしました - - - - The type of the generic parameter '{0}' could not be inferred. - ジェネリック パラメーター '{0}' の型を推論できませんでした。 - - - - The generic test method '{0}' doesn't have arguments, so the generic parameter cannot be inferred. - '{0}' ジェネリック テスト メソッドに引数がないため、ジェネリック パラメーターを推論できません。 - - - - Found two conflicting types for generic parameter '{0}'. The conflicting types are '{1}' and '{2}'. - ジェネリック パラメーター '{0}' に 2 つの競合する型が見つかりました。競合する型は '{1}' で '{2}'。 - - - - Invalid value '{0}' for runsettings entry '{1}', setting will be ignored. - runsettings エントリ '{1}' の値 '{0}' は無効です。設定は無視されます。 - - - - Runsettings entry '<ExecutionApartmentState>STA</ExecutionApartmentState>' is not supported on non-Windows OSes. - Runsettings エントリ '<ExecutionApartmentState>STA</ExecutionApartmentState>' は、Windows OS 以外ではサポートされていません。 - - - - Running tests in any of the provided sources is not supported for the selected platform - 指定されたソースのいずれかでのテストの実行は、選択されたプラットフォームでサポートされていません - - - - Test cleanup method '{0}.{1}' timed out after {2}ms - テスト クリーンアップ メソッド '{0}.{1}' が {2}ms 後にタイムアウトしました - - - - Test cleanup method '{0}.{1}' was canceled - テスト クリーンアップ メソッド '{0}.{1}' が取り消されました - - - - Test initialize method '{0}.{1}' timed out after {2}ms - テスト初期化メソッド '{0}.{1}' が {2}ms 後にタイムアウトになりました - - - - Test initialize method '{0}.{1}' was canceled - テスト初期化メソッド '{0}.{1}' が取り消されました - - - - TestCleanup method {0}.{1} threw exception. {2}. - TestCleanup メソッド {0}.{1} は例外をスローしました。{2}。 - - - - --- End of inner exception stack trace --- - --- 内部例外スタック トレースの終わり --- - - - - UTA014: {0}: Cannot define more than one method with the AssemblyCleanup attribute inside an assembly. - UTA014: {0}: 1 つのアセンブリ内で、AssemblyCleanup 属性を伴う 2 つ以上のメソッドを定義することはできません。 - - - - UTA013: {0}: Cannot define more than one method with the AssemblyInitialize attribute inside an assembly. - UTA013: {0}: 1 つのアセンブリ内で、AssemblyInitialize 属性を伴う 2 つ以上のメソッドを定義することはできません。 - - - - UTA026: {0}: Cannot define more than one method with the ClassCleanup attribute inside a class. - UTA026: {0}: 1 つのクラス内で、ClassCleanup 属性を伴う 2 つ以上のメソッドを定義することはできません。 - - - - UTA025: {0}: Cannot define more than one method with the ClassInitialize attribute inside a class. - UTA025: {0}: 1 つのクラス内で、ClassInitialize 属性を伴う 2 つ以上のメソッドを定義することはできません。 - - - - UTA024: {0}: Cannot define more than one method with the TestCleanup attribute. - UTA024: {0}: TestCleanup 属性を伴う 2 つ以上のメソッドを定義することはできません。 - - - - UTA018: {0}: Cannot define more than one method with the TestInitialize attribute. - UTA018: {0}: TestInitialize 属性を伴う 2 つ以上のメソッドを定義することはできません。 - - - - TestClass attribute defined on generic non-abstract class {0} - 汎用の非抽象クラス {0}で定義された TestClass 属性 - - - - Initialization method {0}.{1} threw exception. {2}. - 初期化メソッド {0}.{1} は例外をスローしました。{2}。 - - - - Unable to create instance of class {0}. Error: {1}. - クラス {0} のインスタンスを作成できません。エラー: {1}。 - - - - The test method '{0}.{1}' has multiple attributes derived from '{2}' defined on it. Only one such attribute is allowed. - テスト メソッド '{0}.{1}' には、 '{2}' から派生した属性が複数定義されています。このような属性は 1 つしか許可されません。 - - - - Cannot find a valid constructor for test class '{0}'. Valid constructors are 'public' and either parameterless or with one parameter of type 'TestContext'. - テスト クラス '{0}' の有効なコンストラクターが見つかりません。有効なコンストラクターは、'public' で、パラメーターがないもの、または 'TestContext' 型のパラメーター 1 個を取るものです。 - - - - Unable to set TestContext property for the class {0}. Error: {1}. - クラス {0} の TestContext プロパティを設定できません。エラー: {1}。 - - - - (Failed to get the message for an exception of type {0} due to an exception.) - (例外が発生したため、型 {0} の例外のメッセージを取得できませんでした。) - - - - UTA031: class {0} does not have valid TestContext property. TestContext must be of type TestContext, must be non-static, and must be public. For example: public TestContext TestContext. - UTA031: クラス {0} に有効な TestContext プロパティがありません。TestContext は TestContext 型で、非静的である必要があり、public である必要があります。たとえば、public TestContext TestContext です。 - - - - UTA001: TestClass attribute defined on non-public class {0} - UTA001: TestClass 属性がパブリックでないクラス {0} で定義されています - - - - MSTestAdapter failed to discover tests in class '{0}' of assembly '{1}' because {2}. - MSTestAdapter でアセンブリ '{1}' のクラス '{0}' にテストが見つかりませんでした。理由 {2}。 - - - - {0}: {1} - {0}: {1} - - - - Unable to load types from the test source '{0}'. Some or all of the tests in this source may not be discovered. -Error: {1} - テスト ソース '{0}' から型を読み込むことができません。このソース内の一部またはすべてのテストが見つからない可能性があります。 -エラー: {1} - - - - File does not exist: {0} - ファイルが存在しません: {0} - - - - UTA007: Method {1} defined in class {0} does not have correct signature. Test method marked with the [TestMethod] attribute must be non-static, public, return-type as void and should not take any parameter. Example: public void Test.Class1.Test(). Additionally, if you are using async-await in test method then return-type must be 'Task' or 'ValueTask'. Example: public async Task Test.Class1.Test2() - UTA007: クラス {0} で定義されているメソッド {1} に適切なシグネチャが含まれていません。[TestMethod] 属性でマークされたテスト メソッドは、non-static および public である必要があり、戻り値の型は void である必要があります。また、パラメーターを受け取ることはできません。例: public void Test.Class1.Test()。また、テスト メソッドで async-await を使用している場合、戻り値の型は 'Task' または 'ValueTask' である必要があります。例: public async Task Test.Class1.Test2() - - - - TestContext cannot be Null. - TestContext を Null にすることはできません。 - - - - Assembly Cleanup method {0}.{1} failed. Error Message: {2}. StackTrace: {3} - アセンブリ クリーンアップ メソッド {0}.{1} に失敗しました。エラー メッセージ: {2}。スタック トレース: {3} - - - - Assembly Initialization method {0}.{1} threw exception. {2}: {3}. Aborting test execution. - アセンブリ初期化メソッド {0}.{1} は例外をスローしました。{2}: {3}。テストの実行を中止しています。 - - - - Class Cleanup method {0}.{1} failed. Error Message: {2}. Stack Trace: {3} - クラス クリーンアップ メソッド {0}.{1} に失敗しました。エラー メッセージ: {2}。スタック トレース: {3} - - - - Class Initialization method {0}.{1} threw exception. {2}: {3}. - クラス初期化メソッド {0}.{1} は例外をスローしました。{2}: {3}。 - - - - An unhandled exception was thrown by the 'Execute' method. Please report this error to the author of the attribute '{0}'. -{1} - 'Execute' メソッドによってハンドルされない例外がスローされました。属性 '{0}' の作成者にこのエラーを報告してください。 -{1} - - - - Error in executing test. No result returned by extension. If using extension of TestMethodAttribute then please contact vendor. - テストの実行中にエラーが発生しました。拡張から結果が返されませんでした。TestMethodAttribute の拡張クラスを使用している場合は、ベンダーに連絡してください。 - - - - Method {0}.{1} has wrong signature. The method must be static, public, does not return a value and should not take any parameter. Additionally, if you are using async-await in method then return-type must be 'Task' or 'ValueTask'. - メソッド {0}。{1} は不適切なシグネチャを含んでいます。メソッドは static および public である必要があり、値を返しません。また、パラメーターを受け取ることはできません。また、メソッドで async-await を使用している場合、戻り値の型は 'Task' または 'ValueTask' である必要があります。 - - - - Method {0}.{1} has wrong signature. The method must be static, public, does not return a value and should take a single parameter of type TestContext. Additionally, if you are using async-await in method then return-type must be 'Task' or 'ValueTask'. - メソッド {0}。{1} は不適切なシグネチャを含んでいます。メソッドは static および public である必要があり、値を返しません。また、TestContext 型の 1 つのパラメーターを受け取る必要があります。また、メソッドで async-await を使用している場合、戻り値の型は 'Task' または 'ValueTask' である必要があります。 - - - - UTA054: {0}.{1} has invalid Timeout attribute. The timeout must be an integer value greater than 0. - UTA054: {0}。{1} に無効な Timeout 属性があります。タイムアウトには、0 より大きい整数値を指定する必要があります。 - - - - UTA023: {0}: Cannot define predefined property {2} on method {1}. - UTA023: {0}: メソッド {1} 上の以前に定義されたプロパティ {2} を定義することはできません。 - - - - UTA021: {0}: Null or empty custom property defined on method {1}. The custom property must have a valid name. - UTA021: {0}: Null または空のカスタム プロパティが、メソッド {1} で定義されています。カスタム プロパティには有効な名前を指定しなければなりません。 - - - - Method {0}.{1} does not exist. - メソッド {0}.{1} は存在しません。 - - - - Unable to find property {0}.TestContext. Error:{1}. - プロパティ {0}.TestContext が見つかりません。エラー: {1}。 - - - - The {0}.TestContext has incorrect type. - {0}.TestContext は不適切な型を含んでいます。 - - - - Method {0}.{1} has wrong signature. The method must be non-static, public, does not return a value and should not take any parameter. Additionally, if you are using async-await in method then return-type must be 'Task' or 'ValueTask'. - メソッド {0}。{1} は不適切なシグネチャを含んでいます。メソッドは non-static および public である必要があり、値を返しません。また、パラメーターを受け取ることはできません。また、メソッドで async-await を使用している場合、戻り値の型は 'Task' または 'ValueTask' である必要があります。 - - - - Unable to get type {0}. Error: {1}. - 型 {0} を取得できません。エラー: {1}。 - - - - Test method {0} was not found. - テスト メソッド {0} が見つかりませんでした。 - - - - Debug Trace: - デバッグ トレース: - - - - Failed to obtain the exception thrown by test method {0}.{1}. - テスト メソッド {0}.{1} によってスローされた例外を取得できませんでした。 - - - - Test method {0}.{1} threw exception: -{2} - テスト メソッド {0}.{1} が例外をスローしました: -{2} - - - - {0} For UWP projects, if you are using UI objects in test consider using [UITestMethod] attribute instead of [TestMethod] to execute test in UI thread. - {0} UWP プロジェクトについて、テスト内で UI オブジェクトを使用している場合は、[TestMethod] の代わりに [UITestMethod] 属性を使用して UI スレッド内でテストを実行することを検討してください。 - - - - MSTestAdapterV2 - MSTestAdapterV2 - - - - Invalid settings '{0}'. Unexpected XmlAttribute: '{1}'. - 設定 '{0}' は無効です。予期しない XmlAttribute: '{1}'。 - - - - Invalid settings '{0}'. Unexpected XmlElement: '{1}'. - 設定 '{0}' は無効です。予期しない XmlElement: '{1}'。 - - - - {0} (Data Row {1}) - {0} (データ行 {1}) - - - - The ExpectedException attribute defined on test method {0}.{1} threw an exception during construction. -{2} - テスト メソッド {0}.{1} に定義されている ExpectedException 属性が、作成中に例外をスローしました。 -{2} - - - - Warning : A testsettings file or a vsmdi file is not supported with the MSTest V2 Adapter. - 警告: testsettings ファイル、vsmdi ファイルは MSTest V2 アダプターではサポートされていません。 - - - - TestContext Messages: - TestContext メッセージ: - - - - Error calling Test Cleanup method for test class {0}: {1} - テスト クラス {0} のテスト クリーンアップ メソッドの呼び出しでエラーが発生しました: {1} - - - - TestCleanup Stack Trace - TestCleanup スタック トレース - - - - [MSTest][Discovery][{0}] {1} - [MSTest][Discovery][{0}] {1} - - - - Test Parallelization enabled for {0} (Workers: {1}, Scope: {2}) - {0} でテスト並列処理が有効にされています (Workers: {1}、Scope: {2})。 - `Workers` is a setting name that shouldn't be localized. 'Scope' is a setting name that shouldn't be localized. - - - Invalid value '{0}' specified for 'Scope'. Supported scopes are {1}. - 無効な値 '{0}' が 'Scope' に指定されました。サポートされているスコープは {1} です。 - 'Scope' is a setting name that shouldn't be localized. - - - Invalid value '{0}' specified for 'Workers'. The value should be a non-negative integer. - 無効な値 '{0}' が 'Workers' に指定されました。値は負ではない整数である必要があります。 - `Workers` is a setting name that shouldn't be localized. - - - Failed to discover tests from assembly {0}. Reason:{1} - アセンブリ {0} からテストを検出できませんでした。理由:{1} - - - - Test '{0}' was canceled - テスト '{0}' が取り消されました - - - - Invalid value '{0}' specified for 'ClassCleanupLifecycle'. Supported scopes are {1}. - 'ClassCleanupLifecycle' に無効な値 '{0}' が指定されました。サポートされているスコープは {1} です。 - 'ClassCleanupLifecycle' is a setting name that shouldn't be localized. - - - Exception occurred while enumerating IDataSource attribute on "{0}.{1}": {2} - "{0}.{1}" で IDataSource 属性を列挙中に例外が発生しました: {2} - {0}: TypeName with namespace, -{1}: Method name, -{2}: Exception details - - - "{0}": (Failed to get exception description due to an exception of type "{1}". - "{0}": (種類が "{1}"の例外のため、例外の説明を取得できませんでした。 - {0}: Type of the original exception that we're trying to get the description of. -{1}: Thrown exception - - - Exceptions thrown: - スローされた例外: - This is usually precedes by TestAssembly_AssemblyDiscoveryFailure message, and preceded by list of exceptions thrown in a test discovery session. - - - Getting custom attributes for type {0} threw exception (will ignore and use the reflection way): {1} - 型 {0} のカスタム属性を取得中に例外がスローされました (無視してリフレクションの方法を使用します): {1} - {0}: Attribute full type name. -{1}: Exception description - - - An older version of MSTestV2 package is loaded in assembly, test discovery might fail to discover all data tests if they depend on `.runsettings` file. - 古いバージョンの MSTestV2 パッケージがアセンブリに読み込まれています。`.runsettings` ファイルに依存している場合、テスト検出ですべてのデータ テストを検出できない可能性があります。 - - - - The called code threw an exception that was caught, but the exception value was null - 呼び出されたコードはキャッチされた例外をスローしましたが、例外値が null でした - - - - Exception occurred while expanding IDataSource rows from attribute on "{0}.{1}": {2} - "{0}.{1}" の属性から IDataSource 行を展開中に例外が発生しました: {2} - {0}: TypeName with namespace, -{1}: Method name, -{2}: CannotExpandIDataSourceAttribute_DuplicateDisplayName or CannotExpandIDataSourceAttribute_CannotSerialize - - - Display name "{2}" on indexes {0} and {1} are duplicate. Display names should be unique. - インデックス {0} と {1} の表示名 "{2}" が重複しています。表示名は一意である必要があります。 - {0}, {1}: Zero based index if an element inside of an array -{2}: Test display name. - - - Data on index {0} for "{1}" cannot be serialized. All data provided through "IDataSource" should be serializable. If you need to test non-serializable data sources, please make sure you add "TestDataSourceDiscovery" attribute on your test assembly and set the discovery option to "DuringExecution". - "{1}" のインデックス {0} のデータをシリアル化できません。"IDataSource" を介して提供されるすべてのデータはシリアル化可能である必要があります。シリアル化できないデータ ソースをテストする必要がある場合は、テスト アセンブリに "TestDataSourceDiscovery" 属性を追加し、検出オプションを "DuringExecution" に設定してください。 - {0}: Zero based index if an element inside of an array, -{1}: Test name - - - - \ No newline at end of file diff --git a/src/Adapter/MSTest.TestAdapter/Resources/xlf/Resource.ko.xlf b/src/Adapter/MSTest.TestAdapter/Resources/xlf/Resource.ko.xlf deleted file mode 100644 index b652734ad5..0000000000 --- a/src/Adapter/MSTest.TestAdapter/Resources/xlf/Resource.ko.xlf +++ /dev/null @@ -1,482 +0,0 @@ - - - - - - Assembly cleanup method '{0}.{1}' timed out after {2}ms - {2}밀리초 후 어셈블리 정리 메서드 '{0}.{1}'이(가) 시간 초과되었습니다. - - - - Assembly cleanup method '{0}.{1}' was canceled - 어셈블리 정리 메서드 '{0}.{1}'이(가) 취소되었습니다. - - - - Assembly initialize method '{0}.{1}' timed out after {2}ms - {2}밀리초 후 '{0}.{1}' 어셈블리 초기화 메서드의 시간이 초과되었습니다. - - - - Assembly initialize method '{0}.{1}' was canceled - 어셈블리 초기화 메서드 '{0}.{1}'(이)가 취소되었습니다. - - - - Cannot run test method '{0}.{1}': Test data doesn't match method parameters. Either the count or types are different. -Test expected {2} parameter(s), with types '{3}', -but received {4} argument(s), with types '{5}'. - 테스트 메서드 '{0}{1}'을(를) 실행할 수 없음: 테스트 데이터가 메서드 매개 변수와 일치하지 않습니다. 개수 또는 형식이 다릅니다. -테스트에 {2} 매개 변수로 ‘{3}’ 형식이 필요하지만, -{4} 인수의 ‘{5}’ 형식을 받았습니다. - - - - Cannot run test method '{0}.{1}': Method has parameters, but does not define any test source. Use '[DataRow]', '[DynamicData]', or a custom 'ITestDataSource' data source to provide test data. - 테스트 메서드 '{0}{1}'을(를) 실행할 수 없음: 메서드에 매개 변수가 있지만 테스트 원본을 정의하지 않습니다. '[DataRow]', '[DynamicData]' 또는 사용자 지정 'ITestDataSource' 데이터 원본을 사용하여 테스트 데이터를 제공합니다. - - - - Class cleanup method '{0}.{1}' timed out after {2}ms - {2}밀리초 후 클래스 정리 메서드 '{0}.{1}'이(가) 시간 초과되었습니다. - - - - Class cleanup method '{0}.{1}' was canceled - 클래스 정리 메서드 '{0}.{1}'이(가) 취소되었습니다. - - - - Class initialize method '{0}.{1}' timed out after {2}ms - {2}밀리초 후 '{0}.{1}' 클래스 초기화 메서드의 시간이 초과되었습니다. - - - - Class initialize method '{0}.{1}' was canceled - '클래스 초기화 메서드 '{0}.{1}'이(가) 취소되었습니다. - - - - Both '.runsettings' and '.testconfig.json' files have been detected. Please select only one of these test configuration files. - '.runsettings' 및 '.testconfig.json' 파일이 모두 검색되었습니다. 이러한 테스트 구성 파일 중 하나만 선택하세요. - - - - Test '{0}' timed out after {1}ms - 테스트 '{0}' {1}밀리초 후에 시간 초과되었습니다. - - - - The type of the generic parameter '{0}' could not be inferred. - 제네릭 매개 변수 '{0}' 형식을 유추할 수 없습니다. - - - - The generic test method '{0}' doesn't have arguments, so the generic parameter cannot be inferred. - 제네릭 테스트 메서드 '{0}' 인수가 없으므로 제네릭 매개 변수를 유추할 수 없습니다. - - - - Found two conflicting types for generic parameter '{0}'. The conflicting types are '{1}' and '{2}'. - 제네릭 매개 변수 '{0}' 충돌하는 두 가지 형식을 찾았습니다. 충돌하는 형식은 '{1}' '{2}'. - - - - Invalid value '{0}' for runsettings entry '{1}', setting will be ignored. - runsettings 항목 '{1}'에 대해 '{0}' 값이 잘못되었습니다. 설정은 무시됩니다. - - - - Runsettings entry '<ExecutionApartmentState>STA</ExecutionApartmentState>' is not supported on non-Windows OSes. - Runsettings 항목 '<ExecutionApartmentState>STA</ExecutionApartmentState>'는 Windows 이외의 OS에서는 지원되지 않습니다. - - - - Running tests in any of the provided sources is not supported for the selected platform - 선택된 플랫폼의 경우 제공된 소스에서 테스트를 실행할 수 없습니다. - - - - Test cleanup method '{0}.{1}' timed out after {2}ms - {2}밀리초 후 테스트 정리 메서드 '{0}.{1}'이(가) 시간 초과되었습니다. - - - - Test cleanup method '{0}.{1}' was canceled - 테스트 정리 메서드 '{0}.{1}'이(가) 취소되었습니다. - - - - Test initialize method '{0}.{1}' timed out after {2}ms - {2}밀리초 후 '{0}.{1}' 테스트 초기화 메서드의 시간이 초과되었습니다. - - - - Test initialize method '{0}.{1}' was canceled - 테스트 초기화 메서드 '{0}.{1}'이(가) 취소되었습니다. - - - - TestCleanup method {0}.{1} threw exception. {2}. - TestCleanup 메서드 {0}.{1}에서 예외가 발생했습니다. {2}. - - - - --- End of inner exception stack trace --- - --- 내부 예외 스택 추적의 끝 --- - - - - UTA014: {0}: Cannot define more than one method with the AssemblyCleanup attribute inside an assembly. - UTA014: {0}: 어셈블리 내부에서 AssemblyCleanup 특성을 사용하는 메서드를 여러 개 정의할 수 없습니다. - - - - UTA013: {0}: Cannot define more than one method with the AssemblyInitialize attribute inside an assembly. - UTA013: {0}: 어셈블리 내부에서 AssemblyInitialize 특성을 사용하는 메서드를 여러 개 정의할 수 없습니다. - - - - UTA026: {0}: Cannot define more than one method with the ClassCleanup attribute inside a class. - UTA026: {0}: 클래스 내부에서 ClassCleanup 특성을 사용하는 메서드를 여러 개 정의할 수 없습니다. - - - - UTA025: {0}: Cannot define more than one method with the ClassInitialize attribute inside a class. - UTA025: {0}: 클래스 내부에서 ClassInitialize 특성을 사용하는 메서드를 여러 개 정의할 수 없습니다. - - - - UTA024: {0}: Cannot define more than one method with the TestCleanup attribute. - UTA024: {0}: TestCleanup 특성을 사용하는 메서드를 여러 개 정의할 수 없습니다. - - - - UTA018: {0}: Cannot define more than one method with the TestInitialize attribute. - UTA018: {0}: TestInitialize 특성을 사용하는 메서드를 여러 개 정의할 수 없습니다. - - - - TestClass attribute defined on generic non-abstract class {0} - 일반 비추상 클래스 {0}에 정의된 TestClass 속성 - - - - Initialization method {0}.{1} threw exception. {2}. - 초기화 메서드 {0}.{1}에서 예외를 throw했습니다. {2}. - - - - Unable to create instance of class {0}. Error: {1}. - {0} 클래스의 인스턴스를 만들 수 없습니다. 오류: {1} - - - - The test method '{0}.{1}' has multiple attributes derived from '{2}' defined on it. Only one such attribute is allowed. - 테스트 메서드 '{0}.{1}'에 {2}에서 파생된 여러 특성이 정의되어 있습니다. 이러한 특성은 하나만 허용됩니다. - - - - Cannot find a valid constructor for test class '{0}'. Valid constructors are 'public' and either parameterless or with one parameter of type 'TestContext'. - 테스트 클래스 '{0}'에 대한 유효한 생성자를 찾을 수 없습니다. 유효한 생성자는 'public'이며 매개 변수가 없거나 'TestContext' 유형의 매개 변수가 하나 있습니다. - - - - Unable to set TestContext property for the class {0}. Error: {1}. - {0} 클래스에 대해 TestContext 속성을 설정할 수 없습니다. 오류: {1} - - - - (Failed to get the message for an exception of type {0} due to an exception.) - (예외로 인해 {0} 형식의 예외에 대한 메시지를 가져오지 못했습니다.) - - - - UTA031: class {0} does not have valid TestContext property. TestContext must be of type TestContext, must be non-static, and must be public. For example: public TestContext TestContext. - UTA031: {0} 클래스에 유효한 TestContext 속성이 없습니다. TestContext는 TestContext 유형이어야 하고, 정적이 아니어야 하며, 일반적이어야 합니다. 예: public TestContext TestContext. - - - - UTA001: TestClass attribute defined on non-public class {0} - UTA001: public이 아닌 클래스 {0}에서 TestClass 특성을 정의했습니다. - - - - MSTestAdapter failed to discover tests in class '{0}' of assembly '{1}' because {2}. - MSTestAdapter가 {2} 때문에 어셈블리 '{1}'의 클래스 '{0}'에서 테스트를 검색하지 못했습니다. - - - - {0}: {1} - {0}: {1} - - - - Unable to load types from the test source '{0}'. Some or all of the tests in this source may not be discovered. -Error: {1} - 테스트 소스 '{0}'에서 형식을 로드할 수 없습니다. 이 소스의 일부 또는 모든 테스트를 검색할 수 없습니다. -오류: {1} - - - - File does not exist: {0} - 파일이 없습니다. {0} - - - - UTA007: Method {1} defined in class {0} does not have correct signature. Test method marked with the [TestMethod] attribute must be non-static, public, return-type as void and should not take any parameter. Example: public void Test.Class1.Test(). Additionally, if you are using async-await in test method then return-type must be 'Task' or 'ValueTask'. Example: public async Task Test.Class1.Test2() - UTA007: 클래스 {0}에 정의된 메서드 {1}에 올바른 서명이 없습니다. [TestMethod] 특성으로 표시된 테스트 메서드는 non-static, public, return-type이 void여야 하며 매개 변수를 사용하지 않아야 합니다. 예: public void Test.Class1.Test(). 또한 테스트 메서드에서 비동기 대기를 사용하는 경우 반환 형식은 Task여야 합니다. 예: public async Task Test.Class1.Test2() - - - - TestContext cannot be Null. - TestContext는 null일 수 없습니다. - - - - Assembly Cleanup method {0}.{1} failed. Error Message: {2}. StackTrace: {3} - 어셈블리 정리 메서드 {0}.{1}이(가) 실패했습니다. 오류 메시지: {2}. StackTrace: {3} - - - - Assembly Initialization method {0}.{1} threw exception. {2}: {3}. Aborting test execution. - 어셈블리 초기화 메서드 {0}.{1}에서 예외를 throw했습니다. {2}: {3}. 테스트 실행을 중단합니다. - - - - Class Cleanup method {0}.{1} failed. Error Message: {2}. Stack Trace: {3} - 클래스 정리 메서드 {0}.{1}이(가) 실패했습니다. 오류 메시지: {2}. 스택 추적: {3} - - - - Class Initialization method {0}.{1} threw exception. {2}: {3}. - 클래스 초기화 메서드 {0}.{1}에서 예외를 throw했습니다. {2}: {3} - - - - An unhandled exception was thrown by the 'Execute' method. Please report this error to the author of the attribute '{0}'. -{1} - 'Execute' 메서드에서 처리되지 않은 예외가 throw되었습니다. 특성 '{0}' 만든 이에게 이 오류를 보고하십시오. -{1} - - - - Error in executing test. No result returned by extension. If using extension of TestMethodAttribute then please contact vendor. - 테스트를 실행하는 중에 오류가 발생했습니다. 확장에서 결과가 반환되지 않았습니다. TestMethodAttribute 확장을 사용하는 경우 공급업체에 문의하세요. - - - - Method {0}.{1} has wrong signature. The method must be static, public, does not return a value and should not take any parameter. Additionally, if you are using async-await in method then return-type must be 'Task' or 'ValueTask'. - {0}.{1} 메서드의 서명이 잘못되었습니다. 메서드는 정적이고 공용이어야 하며, 값을 반환하거나 매개 변수를 취하지 않습니다. 또한 메서드에서 비동기 대기를 사용하는 경우 반환 형식은 'Task' 또는 'ValueTask'여야 합니다. - - - - Method {0}.{1} has wrong signature. The method must be static, public, does not return a value and should take a single parameter of type TestContext. Additionally, if you are using async-await in method then return-type must be 'Task' or 'ValueTask'. - {0}.{1} 메서드의 서명이 잘못되었습니다. 메서드는 static, public이어야 하고, 값을 반환하지 않으며, TestContext 형식의 단일 매개 변수를 사용해야 합니다. 또한 메서드에서 비동기 대기를 사용하는 경우 반환 형식은 'Task' 또는 'ValueTask'여야 합니다. - - - - UTA054: {0}.{1} has invalid Timeout attribute. The timeout must be an integer value greater than 0. - UTA054: {0}.{1}에 잘못된 Timeout 특성이 있습니다. 시간 제한 값은 0보다 큰 정수여야 합니다. - - - - UTA023: {0}: Cannot define predefined property {2} on method {1}. - UTA023: {0}: {1} 메서드에서 미리 정의된 속성 {2}을(를) 정의할 수 없습니다. - - - - UTA021: {0}: Null or empty custom property defined on method {1}. The custom property must have a valid name. - UTA021: {0}: {1} 메서드에서 Null 또는 빈 사용자 지정 속성을 정의했습니다. 사용자 지정 속성에는 올바른 이름이 지정되어 있어야 합니다. - - - - Method {0}.{1} does not exist. - {0}.{1} 메서드가 없습니다. - - - - Unable to find property {0}.TestContext. Error:{1}. - {0}.TestContext 속성을 찾을 수 없습니다. 오류: {1} - - - - The {0}.TestContext has incorrect type. - {0}.TestContext의 형식이 잘못되었습니다. - - - - Method {0}.{1} has wrong signature. The method must be non-static, public, does not return a value and should not take any parameter. Additionally, if you are using async-await in method then return-type must be 'Task' or 'ValueTask'. - {0}.{1} 메서드의 서명이 잘못되었습니다. 메서드는 정적이 아니고 공용이어야 하며, 값을 반환하거나 매개 변수를 사용할 수 없습니다. 또한 메서드에서 비동기 대기를 사용하는 경우 반환 형식은 'Task' 또는 'ValueTask'여야 합니다. - - - - Unable to get type {0}. Error: {1}. - {0} 형식을 가져올 수 없습니다. 오류: {1} - - - - Test method {0} was not found. - {0} 테스트 메서드를 찾을 수 없습니다. - - - - Debug Trace: - 디버그 추적: - - - - Failed to obtain the exception thrown by test method {0}.{1}. - 테스트 메서드 {0}.{1}에서 throw한 예외를 가져오지 못했습니다. - - - - Test method {0}.{1} threw exception: -{2} - 테스트 메서드 {0}.{1}에서 예외를 throw했습니다. -{2} - - - - {0} For UWP projects, if you are using UI objects in test consider using [UITestMethod] attribute instead of [TestMethod] to execute test in UI thread. - {0} UWP 프로젝트의 경우 테스트에서 UI 개체를 사용 중이면 [TestMethod] 대신 [UITestMethod] 특성을 사용하여 UI 스레드에서 테스트를 실행하세요. - - - - MSTestAdapterV2 - MSTestAdapterV2 - - - - Invalid settings '{0}'. Unexpected XmlAttribute: '{1}'. - '{0}' 설정이 잘못되었습니다. 예기치 않은 XmlAttribute '{1}'이(가) 있습니다. - - - - Invalid settings '{0}'. Unexpected XmlElement: '{1}'. - '{0}' 설정이 잘못되었습니다. 예기치 않은 XmlElement '{1}'이(가) 있습니다. - - - - {0} (Data Row {1}) - {0}(데이터 행 {1}) - - - - The ExpectedException attribute defined on test method {0}.{1} threw an exception during construction. -{2} - 테스트 메서드 {0}.{1}에 정의된 ExpectedException 특성이 생성하는 동안 예외를 throw했습니다. -{2} - - - - Warning : A testsettings file or a vsmdi file is not supported with the MSTest V2 Adapter. - 경고: MSTest V2 어댑터에서는 testsettings 파일 또는 vsmdi 파일이 지원되지 않습니다. - - - - TestContext Messages: - TestContext 메시지: - - - - Error calling Test Cleanup method for test class {0}: {1} - 테스트 클래스 {0}에 대한 테스트 정리 메서드를 호출하는 오류: {1} - - - - TestCleanup Stack Trace - TestCleanup 스택 추적 - - - - [MSTest][Discovery][{0}] {1} - [MSTest][검색][{0}] {1} - - - - Test Parallelization enabled for {0} (Workers: {1}, Scope: {2}) - {0}에 대해 테스트 병렬 처리를 사용합니다(Workers: {1}, Scope: {2}). - `Workers` is a setting name that shouldn't be localized. 'Scope' is a setting name that shouldn't be localized. - - - Invalid value '{0}' specified for 'Scope'. Supported scopes are {1}. - 'Scope'에 대해 잘못된 값 '{0}'이(가) 지정되었습니다. 지원되는 범위는 {1}입니다. - 'Scope' is a setting name that shouldn't be localized. - - - Invalid value '{0}' specified for 'Workers'. The value should be a non-negative integer. - 'Workers'에 대해 잘못된 값 '{0}'이(가) 지정되었습니다. 이 값은 음수가 아닌 정수여야 합니다. - `Workers` is a setting name that shouldn't be localized. - - - Failed to discover tests from assembly {0}. Reason:{1} - 어셈블리 {0}에서 테스트를 검색하지 못했습니다. 이유:{1} - - - - Test '{0}' was canceled - 테스트 '{0}' 취소되었습니다. - - - - Invalid value '{0}' specified for 'ClassCleanupLifecycle'. Supported scopes are {1}. - 'ClassCleanupLifecycle'에 대해 '{0}' 잘못된 값이 지정되었습니다. 지원되는 범위는 {1}입니다. - 'ClassCleanupLifecycle' is a setting name that shouldn't be localized. - - - Exception occurred while enumerating IDataSource attribute on "{0}.{1}": {2} - "{0}.{1}"에서 IDataSource 특성을 열거하는 동안 예외가 발생했습니다. {2} - {0}: TypeName with namespace, -{1}: Method name, -{2}: Exception details - - - "{0}": (Failed to get exception description due to an exception of type "{1}". - "{0}": ("{1}" 형식의 예외로 인해 예외 설명을 가져오지 못했습니다. - {0}: Type of the original exception that we're trying to get the description of. -{1}: Thrown exception - - - Exceptions thrown: - 예외 발생: - This is usually precedes by TestAssembly_AssemblyDiscoveryFailure message, and preceded by list of exceptions thrown in a test discovery session. - - - Getting custom attributes for type {0} threw exception (will ignore and use the reflection way): {1} - {0} 형식에 대한 사용자 지정 특성을 가져오는 데 예외가 발생했습니다(무시하고 리플렉션 방법 사용). {1} - {0}: Attribute full type name. -{1}: Exception description - - - An older version of MSTestV2 package is loaded in assembly, test discovery might fail to discover all data tests if they depend on `.runsettings` file. - 이전 버전 MSTestV2 패키지가 어셈블리에 로드되어 테스트 검색이 '.runsettings' 파일에 종속된 경우 모든 데이터 테스트를 검색하지 못할 수 있습니다. - - - - The called code threw an exception that was caught, but the exception value was null - 호출된 코드에서 확인된 예외가 발생했지만 예외 값이 null입니다. - - - - Exception occurred while expanding IDataSource rows from attribute on "{0}.{1}": {2} - "{0}.{1}"의 특성에서 IDataSource 행을 확장하는 동안 예외가 발생했습니다. {2} - {0}: TypeName with namespace, -{1}: Method name, -{2}: CannotExpandIDataSourceAttribute_DuplicateDisplayName or CannotExpandIDataSourceAttribute_CannotSerialize - - - Display name "{2}" on indexes {0} and {1} are duplicate. Display names should be unique. - {0} 및 {1} 인덱스의 "{2}" 표시 이름이 중복됩니다. 표시 이름은 고유해야 합니다. - {0}, {1}: Zero based index if an element inside of an array -{2}: Test display name. - - - Data on index {0} for "{1}" cannot be serialized. All data provided through "IDataSource" should be serializable. If you need to test non-serializable data sources, please make sure you add "TestDataSourceDiscovery" attribute on your test assembly and set the discovery option to "DuringExecution". - "{1}"에 대한 {0} 인덱스의 데이터를 직렬화할 수 없습니다. "IDataSource"를 통해 제공된 모든 데이터를 직렬화할 수 있어야 합니다. 직렬화할 수 없는 데이터 원본을 테스트해야 하는 경우 테스트 어셈블리에 "TestDataSourceDiscovery" 특성을 추가하고 검색 옵션을 "DuringExecution"으로 설정하세요. - {0}: Zero based index if an element inside of an array, -{1}: Test name - - - - \ No newline at end of file diff --git a/src/Adapter/MSTest.TestAdapter/Resources/xlf/Resource.pl.xlf b/src/Adapter/MSTest.TestAdapter/Resources/xlf/Resource.pl.xlf deleted file mode 100644 index e229b5db52..0000000000 --- a/src/Adapter/MSTest.TestAdapter/Resources/xlf/Resource.pl.xlf +++ /dev/null @@ -1,482 +0,0 @@ - - - - - - Assembly cleanup method '{0}.{1}' timed out after {2}ms - Metoda oczyszczania zestawu „{0}.{1}” przekroczyła limit czasu po {2}ms - - - - Assembly cleanup method '{0}.{1}' was canceled - Anulowano metodę oczyszczania 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 - - - - Assembly initialize method '{0}.{1}' was canceled - Anulowano metodę inicjowania zestawu „{0}.{1}” - - - - Cannot run test method '{0}.{1}': Test data doesn't match method parameters. Either the count or types are different. -Test expected {2} parameter(s), with types '{3}', -but received {4} argument(s), with types '{5}'. - Nie można uruchomić metody testowej „{0}.{1}”: dane testowe nie są zgodne z parametrami metody. Liczba lub typy są różne. -Testowanie oczekiwanych {2} parametrów z typami „{3}”, -ale liczba odebranych argumentów to {4} z typami „{5}”. - - - - Cannot run test method '{0}.{1}': Method has parameters, but does not define any test source. Use '[DataRow]', '[DynamicData]', or a custom 'ITestDataSource' data source to provide test data. - Nie można uruchomić metody testowej „{0}.{1}”: metoda ma parametry, ale nie definiuje żadnego źródła testu. Użyj źródła danych „[DataRow]”, „[DynamicData]” lub niestandardowego źródła danych „ITestDataSource”, aby dostarczyć dane testowe. - - - - Class cleanup method '{0}.{1}' timed out after {2}ms - Metoda oczyszczania klasy „{0}.{1}” przekroczyła limit czasu po {2}ms - - - - Class cleanup method '{0}.{1}' was canceled - Anulowano metodę oczyszczania 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 - - - - Class initialize method '{0}.{1}' was canceled - Anulowano metodę inicjowania klasy „{0}.{1}” - - - - Both '.runsettings' and '.testconfig.json' files have been detected. Please select only one of these test configuration files. - Wykryto zarówno pliki „.runsettings”, jak i „.testconfig.json”. Wybierz tylko jeden z tych plików konfiguracji testu. - - - - Test '{0}' timed out after {1}ms - Upłynął limit czasu '{0}' testu po {1}ms - - - - The type of the generic parameter '{0}' could not be inferred. - Nie można wywnioskować typu '{0}' parametru ogólnego. - - - - The generic test method '{0}' doesn't have arguments, so the generic parameter cannot be inferred. - Ogólna metoda testowa '{0}' nie ma argumentów, więc nie można wywnioskować parametru ogólnego. - - - - Found two conflicting types for generic parameter '{0}'. The conflicting types are '{1}' and '{2}'. - Znaleziono dwa typy powodujące konflikt dla parametru ogólnego '{0}'. Typy powodujące konflikty są '{1}' i '{2}'. - - - - Invalid value '{0}' for runsettings entry '{1}', setting will be ignored. - Nieprawidłowa wartość „{0}” dla wpisu runsettings „{1}”. Ustawienie zostanie zignorowane. - - - - Runsettings entry '<ExecutionApartmentState>STA</ExecutionApartmentState>' is not supported on non-Windows OSes. - Wpis runsettings „<ExecutionApartmentState>STA</ExecutionApartmentState>” nie jest obsługiwany w systemach operacyjnych innych niż Windows. - - - - Running tests in any of the provided sources is not supported for the selected platform - Uruchamianie testów w żadnym z podanych źródeł nie jest obsługiwane dla wybranej platformy - - - - Test cleanup method '{0}.{1}' timed out after {2}ms - Metoda oczyszczania testu „{0}.{1}” przekroczyła limit czasu po {2}ms - - - - Test cleanup method '{0}.{1}' was canceled - Anulowano metodę oczyszczania 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 - - - - Test initialize method '{0}.{1}' was canceled - Anulowano metodę inicjowania testu „{0}.{1}” - - - - TestCleanup method {0}.{1} threw exception. {2}. - Metoda TestCleanup {0}.{1} zgłosiła wyjątek {2}. - - - - --- End of inner exception stack trace --- - --- Koniec śledzenia stosu wyjątku wewnętrznego --- - - - - UTA014: {0}: Cannot define more than one method with the AssemblyCleanup attribute inside an assembly. - UTA014: {0}: W zestawie nie można zdefiniować więcej niż jednej metody z atrybutem AssemblyCleanup. - - - - UTA013: {0}: Cannot define more than one method with the AssemblyInitialize attribute inside an assembly. - UTA013: {0}: W zestawie nie można zdefiniować więcej niż jednej metody z atrybutem AssemblyInitialize. - - - - UTA026: {0}: Cannot define more than one method with the ClassCleanup attribute inside a class. - UTA026: {0}: W klasie nie można zdefiniować więcej niż jednej metody z atrybutem ClassCleanup. - - - - UTA025: {0}: Cannot define more than one method with the ClassInitialize attribute inside a class. - UTA025: {0}: W klasie nie można zdefiniować więcej niż jednej metody z atrybutem ClassInitialize. - - - - UTA024: {0}: Cannot define more than one method with the TestCleanup attribute. - UTA024: {0}: Nie można zdefiniować więcej niż jednej metody z atrybutem TestCleanup. - - - - UTA018: {0}: Cannot define more than one method with the TestInitialize attribute. - UTA018: {0}: Nie można zdefiniować więcej niż jednej metody z atrybutem TestInitialize. - - - - TestClass attribute defined on generic non-abstract class {0} - Atrybut TestClass zdefiniowany w ogólnej klasie nieabstrakcyjnej {0} - - - - Initialization method {0}.{1} threw exception. {2}. - Metoda inicjowania {0}.{1} zgłosiła wyjątek. {2}. - - - - Unable to create instance of class {0}. Error: {1}. - Nie można utworzyć wystąpienia klasy {0}. Błąd: {1}. - - - - The test method '{0}.{1}' has multiple attributes derived from '{2}' defined on it. Only one such attribute is allowed. - Metoda testowa "{0}.{1}” ma zdefiniowanych wiele atrybutów pochodzących z „{2}”. Dozwolony jest tylko jeden taki atrybut. - - - - Cannot find a valid constructor for test class '{0}'. Valid constructors are 'public' and either parameterless or with one parameter of type 'TestContext'. - Nie można odnaleźć prawidłowego konstruktora dla klasy testowej „{0}”. Prawidłowe konstruktory są „publiczne” i albo bez parametrów, albo z jednym parametrem typu „TestContext”. - - - - Unable to set TestContext property for the class {0}. Error: {1}. - Nie można ustawić właściwości TestContext w klasie {0}. Błąd: {1}. - - - - (Failed to get the message for an exception of type {0} due to an exception.) - (Nie można pobrać komunikatu dotyczącego wyjątku typu {0} z powodu wyjątku). - - - - UTA031: class {0} does not have valid TestContext property. TestContext must be of type TestContext, must be non-static, and must be public. For example: public TestContext TestContext. - UTA031: Klasa {0}nie ma prawidłowej właściwości TestContext. Element TestContext musi być typu TestContext, musi być niestatyczny i musi być publiczny. Na przykład : public TestContext TestContext. - - - - UTA001: TestClass attribute defined on non-public class {0} - UTA001: Atrybut TestClass zdefiniowany dla niepublicznej klasy {0} - - - - MSTestAdapter failed to discover tests in class '{0}' of assembly '{1}' because {2}. - Adapter MSTestAdapter nie mógł odnaleźć testów w klasie „{0}” zestawu „{1}”, przyczyna: {2}. - - - - {0}: {1} - {0}: {1} - - - - Unable to load types from the test source '{0}'. Some or all of the tests in this source may not be discovered. -Error: {1} - Nie można załadować typów ze źródła testów „{0}”. Niektóre lub wszystkie testy w tym źródle mogły nie zostać odnalezione. -Błąd: {1} - - - - File does not exist: {0} - Plik nie istnieje: {0} - - - - UTA007: Method {1} defined in class {0} does not have correct signature. Test method marked with the [TestMethod] attribute must be non-static, public, return-type as void and should not take any parameter. Example: public void Test.Class1.Test(). Additionally, if you are using async-await in test method then return-type must be 'Task' or 'ValueTask'. Example: public async Task Test.Class1.Test2() - UTA007: metoda {1} zdefiniowana w klasie {0}nie ma poprawnej sygnatury. Metoda testowa oznaczona przez atrybut [TestMethod] musi być niestatyczna, publiczna, zwracać wartość typu void i nie powinna przyjmować żadnego parametru. Przykład: public void Test.Class1.Test(). Ponadto, jeśli używasz oczekiwanie asynchroniczne w metodzie testowej, wtedy zwracanym typem musi być „Task” lub „ValueTask”. Przykład: public async Task Test.Class1.Test2() - - - - TestContext cannot be Null. - Wartość TestContext nie może być równa null. - - - - Assembly Cleanup method {0}.{1} failed. Error Message: {2}. StackTrace: {3} - Metoda czyszczenia zestawu {0}.{1} nie powiodła się. Komunikat o błędzie: {2}. Ślad stosu: {3} - - - - Assembly Initialization method {0}.{1} threw exception. {2}: {3}. Aborting test execution. - Metoda inicjująca zestaw {0}.{1} nie powiodła się. {2}: {3}. Przerywanie wykonywania testu. - - - - Class Cleanup method {0}.{1} failed. Error Message: {2}. Stack Trace: {3} - Metoda czyszczenia klasy {0}.{1} nie powiodła się. Komunikat o błędzie: {2}. Ślad stosu: {3} - - - - Class Initialization method {0}.{1} threw exception. {2}: {3}. - Metoda inicjowania klasy {0}.{1} zgłosiła wyjątek. {2}: {3}. - - - - An unhandled exception was thrown by the 'Execute' method. Please report this error to the author of the attribute '{0}'. -{1} - Metoda Execute zgłosiła nieobsługiwany wyjątek. Zgłoś ten błąd autorowi atrybutu '{0}'. -{1} - - - - Error in executing test. No result returned by extension. If using extension of TestMethodAttribute then please contact vendor. - Błąd podczas wykonywania testu. Rozszerzenie nie zwróciło żadnego wyniku. W przypadku korzystania z rozszerzenia atrybutu TestMethodAttribute należy skontaktować się z dostawcą. - - - - Method {0}.{1} has wrong signature. The method must be static, public, does not return a value and should not take any parameter. Additionally, if you are using async-await in method then return-type must be 'Task' or 'ValueTask'. - Metoda {0}.{1}ma nieprawidłową sygnaturę. Metoda musi być statyczna, publiczna, nie może zwracać wartości i nie powinna przyjmować żadnego parametru. Ponadto jeśli w metodzie jest używane oczekiwanie asynchroniczne, wtedy zwracanym typem musi być typ „Task” lub „ValueTask”. - - - - Method {0}.{1} has wrong signature. The method must be static, public, does not return a value and should take a single parameter of type TestContext. Additionally, if you are using async-await in method then return-type must be 'Task' or 'ValueTask'. - Metoda {0}.{1}ma nieprawidłową sygnaturę. Metoda musi być statyczna, publiczna, nie może zwracać wartości i powinna mieć jeden parametr typu TestContext. Ponadto jeśli w metodzie jest używane oczekiwanie asynchroniczne, wtedy zwracanym typem musi być typ „Task” lub „ValueTask”. - - - - UTA054: {0}.{1} has invalid Timeout attribute. The timeout must be an integer value greater than 0. - UTA054: {0}.{1} ma nieprawidłowy atrybut Timeout. Limit czasu musi być liczbą całkowitą większą niż 0. - - - - UTA023: {0}: Cannot define predefined property {2} on method {1}. - UTA023: {0}: Nie można zdefiniować wstępnie zdefiniowanej właściwości {2} dla metody {1}. - - - - UTA021: {0}: Null or empty custom property defined on method {1}. The custom property must have a valid name. - UTA021: {0}: Zerowa lub pusta niestandardowa właściwość została zdefiniowana dla metody {1}. Niestandardowa właściwość musi mieć prawidłową nazwę. - - - - Method {0}.{1} does not exist. - Metoda {0}.{1} nie istnieje. - - - - Unable to find property {0}.TestContext. Error:{1}. - Nie można znaleźć właściwości {0}.TestContext. Błąd:{1}. - - - - The {0}.TestContext has incorrect type. - Element {0}.TestContext ma niepoprawny typ. - - - - Method {0}.{1} has wrong signature. The method must be non-static, public, does not return a value and should not take any parameter. Additionally, if you are using async-await in method then return-type must be 'Task' or 'ValueTask'. - Metoda {0}.{1}ma nieprawidłową sygnaturę. Metoda musi być niestatyczna, publiczna, nie może zwracać wartości i nie powinna przyjmować żadnego parametru. Ponadto jeśli w metodzie jest używane oczekiwanie asynchroniczne, wtedy zwracanym typem musi być typ „Task” lub „ValueTask”. - - - - Unable to get type {0}. Error: {1}. - Nie można uzyskać typu {0}. Błąd: {1}. - - - - Test method {0} was not found. - Nie znaleziono metody testowej {0}. - - - - Debug Trace: - Ślad debugowania: - - - - Failed to obtain the exception thrown by test method {0}.{1}. - Nie powiodło się uzyskanie wyjątku zgłoszonego przez metodę testową {0}.{1}. - - - - Test method {0}.{1} threw exception: -{2} - Metoda testowa {0}.{1} zgłosiła wyjątek: -{2} - - - - {0} For UWP projects, if you are using UI objects in test consider using [UITestMethod] attribute instead of [TestMethod] to execute test in UI thread. - {0} Jeśli w projektach UWP korzystasz z obiektów interfejsu użytkownika podczas testowania, rozważ użycie atrybutu [UITestMethod] zamiast atrybutu [TestMethod], aby wykonywać test w wątku interfejsu użytkownika. - - - - MSTestAdapterV2 - MSTestAdapterV2 - - - - Invalid settings '{0}'. Unexpected XmlAttribute: '{1}'. - Nieprawidłowe ustawienia „{0}”. Nieoczekiwany atrybut XmlAttribute: „{1}”. - - - - Invalid settings '{0}'. Unexpected XmlElement: '{1}'. - Nieprawidłowe ustawienia „{0}”. Nieoczekiwany atrybut XmlElement: „{1}”. - - - - {0} (Data Row {1}) - {0} (wiersz danych {1}) - - - - The ExpectedException attribute defined on test method {0}.{1} threw an exception during construction. -{2} - Atrybut ExpectedException zdefiniowany dla metody testowej {0}.{1} zgłosił wyjątek w trakcie konstruowania. -{2} - - - - Warning : A testsettings file or a vsmdi file is not supported with the MSTest V2 Adapter. - Ostrzeżenie: Plik testsettings lub plik vsmdi nie jest obsługiwany przez adapter MSTest w wersji 2. - - - - TestContext Messages: - Komunikaty TestContext: - - - - Error calling Test Cleanup method for test class {0}: {1} - Błąd podczas wywoływania metody czyszczącej testu dla klasy testowej {0}: {1} - - - - TestCleanup Stack Trace - Ślad stosu dla TestCleanup - - - - [MSTest][Discovery][{0}] {1} - [MSTest][Discovery][{0}] {1} - - - - Test Parallelization enabled for {0} (Workers: {1}, Scope: {2}) - Przetwarzanie równoległe testów włączono dla {0} (procesy robocze: {1}, zakres: {2}) - `Workers` is a setting name that shouldn't be localized. 'Scope' is a setting name that shouldn't be localized. - - - Invalid value '{0}' specified for 'Scope'. Supported scopes are {1}. - Określono nieprawidłową wartość „{0}” dla właściwości „Scope”. Obsługiwane zakresy to {1}. - 'Scope' is a setting name that shouldn't be localized. - - - Invalid value '{0}' specified for 'Workers'. The value should be a non-negative integer. - Określono nieprawidłową wartość „{0}” dla właściwości „Workers”. Wartość musi być nieujemną liczbą całkowitą. - `Workers` is a setting name that shouldn't be localized. - - - Failed to discover tests from assembly {0}. Reason:{1} - Nie można odnaleźć testów z zestawu {0}. Przyczyna:{1} - - - - Test '{0}' was canceled - Anulowano '{0}' testowe - - - - Invalid value '{0}' specified for 'ClassCleanupLifecycle'. Supported scopes are {1}. - Dla ustawienia „ClassCleanupLifecycle” określono nieprawidłową wartość „{0}”. Obsługiwane zakresy to {1}. - 'ClassCleanupLifecycle' is a setting name that shouldn't be localized. - - - Exception occurred while enumerating IDataSource attribute on "{0}.{1}": {2} - Wystąpił wyjątek podczas wyliczania atrybutu IDataSource w przestrzeni nazwy „{0}.{1}”: {2} - {0}: TypeName with namespace, -{1}: Method name, -{2}: Exception details - - - "{0}": (Failed to get exception description due to an exception of type "{1}". - „{0}”: (Nie można uzyskać opisu wyjątku z powodu wyjątku typu „{1}”. - {0}: Type of the original exception that we're trying to get the description of. -{1}: Thrown exception - - - Exceptions thrown: - Zgłoszone wyjątki: - This is usually precedes by TestAssembly_AssemblyDiscoveryFailure message, and preceded by list of exceptions thrown in a test discovery session. - - - Getting custom attributes for type {0} threw exception (will ignore and use the reflection way): {1} - Pobieranie atrybutów niestandardowych dla typu {0} zgłosiło wyjątek (zignoruje i użyje sposobu odbicia): {1} - {0}: Attribute full type name. -{1}: Exception description - - - An older version of MSTestV2 package is loaded in assembly, test discovery might fail to discover all data tests if they depend on `.runsettings` file. - Starsza wersja pakietu MSTestV2 jest załadowana do zestawu. Odnajdywanie testów może nie odnaleźć wszystkich testów danych, jeśli zależą od pliku „.runsettings”. - - - - The called code threw an exception that was caught, but the exception value was null - Wywołany kod zgłosił wyjątek, który został przechwycony, ale wartość wyjątku miała wartość null - - - - Exception occurred while expanding IDataSource rows from attribute on "{0}.{1}": {2} - Wystąpił wyjątek podczas rozwijania wierszy IDataSource z atrybutu w przestrzeni nazw „{0}.{1}”: {2} - {0}: TypeName with namespace, -{1}: Method name, -{2}: CannotExpandIDataSourceAttribute_DuplicateDisplayName or CannotExpandIDataSourceAttribute_CannotSerialize - - - Display name "{2}" on indexes {0} and {1} are duplicate. Display names should be unique. - Nazwa wyświetlana „{2}” w indeksach {0} i {1} jest duplikatem. Nazwy wyświetlane powinny być unikatowe. - {0}, {1}: Zero based index if an element inside of an array -{2}: Test display name. - - - Data on index {0} for "{1}" cannot be serialized. All data provided through "IDataSource" should be serializable. If you need to test non-serializable data sources, please make sure you add "TestDataSourceDiscovery" attribute on your test assembly and set the discovery option to "DuringExecution". - Nie można serializować danych w indeksie {0} dla „{1}”. Wszystkie dane dostarczane za pomocą źródła „IDataSource” powinny być możliwe do serializacji. Jeśli chcesz przetestować nieserializowalne źródła danych, upewnij się, że dodasz atrybut „ TestDataSourceDiscovery” w zestawie testowym i ustaw opcję odnajdywania na wartość „DuringExecution”. - {0}: Zero based index if an element inside of an array, -{1}: Test name - - - - \ No newline at end of file diff --git a/src/Adapter/MSTest.TestAdapter/Resources/xlf/Resource.pt-BR.xlf b/src/Adapter/MSTest.TestAdapter/Resources/xlf/Resource.pt-BR.xlf deleted file mode 100644 index fbd82fe886..0000000000 --- a/src/Adapter/MSTest.TestAdapter/Resources/xlf/Resource.pt-BR.xlf +++ /dev/null @@ -1,482 +0,0 @@ - - - - - - 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 - - - - Assembly cleanup method '{0}.{1}' was canceled - O método de limpeza do assembly "{0}.{1}" foi cancelado - - - - 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 - - - - Assembly initialize method '{0}.{1}' was canceled - O método de inicialização do assembly "{0}.{1}" foi cancelado - - - - Cannot run test method '{0}.{1}': Test data doesn't match method parameters. Either the count or types are different. -Test expected {2} parameter(s), with types '{3}', -but received {4} argument(s), with types '{5}'. - Não é possível executar o método de teste '{0}.{1}': Os dados de teste não correspondem aos parâmetros do método. A contagem ou os tipos são diferentes. -Testar {2} parâmetros esperados, com tipos '{3}', -mas {4} argumentos recebidos, com tipos '{5}'. - - - - Cannot run test method '{0}.{1}': Method has parameters, but does not define any test source. Use '[DataRow]', '[DynamicData]', or a custom 'ITestDataSource' data source to provide test data. - Não é possível executar o método de teste '{0}.{1}': o método tem parâmetros, mas não define nenhuma fonte de teste. Use '[DataRow]', '[DynamicData]' ou uma fonte de dados 'ITestDataSource' personalizada para fornecer dados de teste. - - - - 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 - - - - Class cleanup method '{0}.{1}' was canceled - O método de limpeza de classe "{0}.{1}" foi cancelado - - - - 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 - - - - Class initialize method '{0}.{1}' was canceled - O método de inicialização de classe "{0}.{1}" foi cancelado - - - - Both '.runsettings' and '.testconfig.json' files have been detected. Please select only one of these test configuration files. - Ambos os arquivos '.runsettings' e '.testconfig.json' foram detectados. Selecione apenas um desses arquivos de configuração de teste. - - - - Test '{0}' timed out after {1}ms - Tempo '{0}' tempo limite do teste após {1}ms - - - - The type of the generic parameter '{0}' could not be inferred. - Não foi possível inferir o tipo '{0}' parâmetro genérico. - - - - The generic test method '{0}' doesn't have arguments, so the generic parameter cannot be inferred. - O método de teste genérico '{0}' não tem argumentos, portanto, o parâmetro genérico não pode ser inferido. - - - - Found two conflicting types for generic parameter '{0}'. The conflicting types are '{1}' and '{2}'. - Foram encontrados dois tipos conflitativos para parâmetro genérico '{0}'. Os tipos conflitativos são '{1}' e '{2}'. - - - - Invalid value '{0}' for runsettings entry '{1}', setting will be ignored. - Valor inválido "{0}" para a entrada runsettings "{1}", a configuração será ignorada. - - - - Runsettings entry '<ExecutionApartmentState>STA</ExecutionApartmentState>' is not supported on non-Windows OSes. - Não há suporte para a entrada runsettings "<ExecutionApartmentState>STA</ExecutionApartmentState>" em sistemas operacionais que não sejam Windows. - - - - Running tests in any of the provided sources is not supported for the selected platform - Não há suporte para execução de teste em qualquer uma das origens fontes fornecidas para a plataforma selecionada - - - - 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 - - - - Test cleanup method '{0}.{1}' was canceled - O método de limpeza de teste "{0}.{1}" foi cancelado - - - - 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 - - - - Test initialize method '{0}.{1}' was canceled - O método de inicialização de teste "{0}.{1}" foi cancelado - - - - TestCleanup method {0}.{1} threw exception. {2}. - O método TestCleanup {0}.{1} gerou a exceção. {2}. - - - - --- End of inner exception stack trace --- - --- Fim do rastreamento de pilha de exceção interna --- - - - - UTA014: {0}: Cannot define more than one method with the AssemblyCleanup attribute inside an assembly. - UTA014: {0}: não é possível definir mais de um método com o atributo AssemblyCleanup em um assembly. - - - - UTA013: {0}: Cannot define more than one method with the AssemblyInitialize attribute inside an assembly. - UTA013: {0}: não é possível definir mais de um método com o atributo AssemblyInitialize dentro de um assembly. - - - - UTA026: {0}: Cannot define more than one method with the ClassCleanup attribute inside a class. - UTA026: {0}: não é possível definir mais de um método com o atributo ClassCleanup dentro de uma classe. - - - - UTA025: {0}: Cannot define more than one method with the ClassInitialize attribute inside a class. - UTA025: {0}: não é possível definir mais de um método com o atributo ClassInitialize em uma classe. - - - - UTA024: {0}: Cannot define more than one method with the TestCleanup attribute. - UTA024: {0}: não é possível definir mais de um método com o atributo TestCleanup. - - - - UTA018: {0}: Cannot define more than one method with the TestInitialize attribute. - UTA018: {0}: não é possível definir mais de um método com o atributo TestInitialize. - - - - TestClass attribute defined on generic non-abstract class {0} - Atributo TestClass definido em uma classe genérica não abstrata {0} - - - - Initialization method {0}.{1} threw exception. {2}. - O método de inicialização {0}.{1} gerou exceção. {2}. - - - - Unable to create instance of class {0}. Error: {1}. - Não é possível criar instância da classe {0}. Erro: {1}. - - - - The test method '{0}.{1}' has multiple attributes derived from '{2}' defined on it. Only one such attribute is allowed. - O método de teste '{0}.{1}' tem várias características derivadas de '{2}' definidas nele. Apenas uma dessas características tem permissão. - - - - Cannot find a valid constructor for test class '{0}'. Valid constructors are 'public' and either parameterless or with one parameter of type 'TestContext'. - Não é possível localizar um construtor válido para a classe de teste '{0}'. Construtores válidos são 'public' e sem parâmetros ou com um parâmetro do tipo 'TestContext'. - - - - Unable to set TestContext property for the class {0}. Error: {1}. - Não é definir a propriedade TestContext para a classe {0}. Erro: {1}. - - - - (Failed to get the message for an exception of type {0} due to an exception.) - (Falha ao obter a mensagem para uma exceção do tipo {0} devido a uma exceção.) - - - - UTA031: class {0} does not have valid TestContext property. TestContext must be of type TestContext, must be non-static, and must be public. For example: public TestContext TestContext. - UTA031: a classe {0} não tem uma propriedade TestContext válida. TestContext deve ser do tipo TestContext, não deve ser estático e deve ser público. Por exemplo: public TestContext TestContext. - - - - UTA001: TestClass attribute defined on non-public class {0} - UTA001: atributo TestClass definido em classe não pública {0} - - - - MSTestAdapter failed to discover tests in class '{0}' of assembly '{1}' because {2}. - O MSTestAdapter não conseguiu descobrir testes na classe '{0}' do assembly '{1}' devido a {2}. - - - - {0}: {1} - {0}: {1} - - - - Unable to load types from the test source '{0}'. Some or all of the tests in this source may not be discovered. -Error: {1} - Não é possível carregar tipos da fonte de teste '{0}'. Alguns ou todos os testes nessa fonte podem não ser descobertos. -Erro: {1} - - - - File does not exist: {0} - O arquivo não existe: {0} - - - - UTA007: Method {1} defined in class {0} does not have correct signature. Test method marked with the [TestMethod] attribute must be non-static, public, return-type as void and should not take any parameter. Example: public void Test.Class1.Test(). Additionally, if you are using async-await in test method then return-type must be 'Task' or 'ValueTask'. Example: public async Task Test.Class1.Test2() - UTA007: o método {1} definido na classe {0} não tem a assinatura correta. O método de teste marcado com o atributo [TestMethod] deve ser não estático, público, com tipo de retorno nulo e não deve receber parâmetros. Exemplo: Test.Class1.Test() público nulo. Além disso, se você estiver usando async-await no método de teste, return-type deverá ser "Task" ou "ValueTask". Exemplo: public async Task Test.Class1.Test2() - - - - TestContext cannot be Null. - TestContext não pode ser Nulo. - - - - Assembly Cleanup method {0}.{1} failed. Error Message: {2}. StackTrace: {3} - Falha no método de Limpeza de Assembly {0}.{1}. Mensagem de Erro: {2}. StackTrace: {3} - - - - Assembly Initialization method {0}.{1} threw exception. {2}: {3}. Aborting test execution. - O método de Inicialização de Assembly {0}.{1} lançou uma exceção. {2}: {3}. Anulando execução de teste. - - - - Class Cleanup method {0}.{1} failed. Error Message: {2}. Stack Trace: {3} - Falha no método de Limpeza de Classe {0}.{1}. Mensagem de Erro: {2}. Rastreamento de Pilha: {3} - - - - Class Initialization method {0}.{1} threw exception. {2}: {3}. - O método de Inicialização de Classe {0}.{1} lançou uma exceção. {2}: {3}. - - - - An unhandled exception was thrown by the 'Execute' method. Please report this error to the author of the attribute '{0}'. -{1} - Uma exceção sem tratamento foi lançada pelo método 'Execute'. Relate este erro ao autor do atributo '{0}'. -{1} - - - - Error in executing test. No result returned by extension. If using extension of TestMethodAttribute then please contact vendor. - Erro ao executar o teste. Nenhum resultado retornado pela extensão. Se usar a extensão de TestMethodAttribute, entre em contato com o fornecedor. - - - - Method {0}.{1} has wrong signature. The method must be static, public, does not return a value and should not take any parameter. Additionally, if you are using async-await in method then return-type must be 'Task' or 'ValueTask'. - O método {0}.{1} tem a assinatura incorreta. O método deve ser estático, público, não deve retornar um valor nem receber parâmetro. Além disso, se você estiver usando async-await no método, o return-type deverá ser "Task" ou "ValueTask". - - - - Method {0}.{1} has wrong signature. The method must be static, public, does not return a value and should take a single parameter of type TestContext. Additionally, if you are using async-await in method then return-type must be 'Task' or 'ValueTask'. - O método {0}.{1} tem a assinatura incorreta. O método deve ser estático, público, não retornar um valor e deve usar um único parâmetro do tipo TestContext. Além disso, se você estiver usando async-await no método, o return-type deverá ser "Task" ou "ValueTask". - - - - UTA054: {0}.{1} has invalid Timeout attribute. The timeout must be an integer value greater than 0. - UTA054: {0}.{1} tem atributo Timeout inválido. O tempo limite deve ser um valor inteiro maior que 0. - - - - UTA023: {0}: Cannot define predefined property {2} on method {1}. - UTA023: {0}: não é possível definir a propriedade predefinida {2} no método {1}. - - - - UTA021: {0}: Null or empty custom property defined on method {1}. The custom property must have a valid name. - UTA021: {0}: Propriedade personalizada nula ou vazia definida no método {1}. A propriedade personalizada deve ter um nome válido. - - - - Method {0}.{1} does not exist. - O método {0}.{1} não existe. - - - - Unable to find property {0}.TestContext. Error:{1}. - Não é possível encontrar propriedade {0}.TestContext. Erro:{1}. - - - - The {0}.TestContext has incorrect type. - O {0}.TestContext é do tipo incorreto. - - - - Method {0}.{1} has wrong signature. The method must be non-static, public, does not return a value and should not take any parameter. Additionally, if you are using async-await in method then return-type must be 'Task' or 'ValueTask'. - O método {0}.{1} tem a assinatura incorreta. O método deve ser não estático, público, não deve retornar um valor e não deve receber nenhum parâmetro. Além disso, se você estiver usando async-await no método, o return-type deverá ser "Task" ou "ValueTask". - - - - Unable to get type {0}. Error: {1}. - Não é possível obter o tipo {0}. Erro: {1}. - - - - Test method {0} was not found. - O método de teste {0} não foi encontrado. - - - - Debug Trace: - Rastreamento de Depuração: - - - - Failed to obtain the exception thrown by test method {0}.{1}. - Falha ao obter a exceção lançada pelo método de teste {0}.{1}. - - - - Test method {0}.{1} threw exception: -{2} - O método de teste {0}.{1} lançou a exceção: -{2} - - - - {0} For UWP projects, if you are using UI objects in test consider using [UITestMethod] attribute instead of [TestMethod] to execute test in UI thread. - {0} Para projetos UWP, se você está usando objetos de IU no teste, considere usar o atributo [UITestMethod] em vez de [TestMethod] para executar o teste no thread da IU. - - - - MSTestAdapterV2 - MSTestAdapterV2 - - - - Invalid settings '{0}'. Unexpected XmlAttribute: '{1}'. - Configurações inválidas '{0}'. XmlAttribute inesperado: '{1}'. - - - - Invalid settings '{0}'. Unexpected XmlElement: '{1}'. - Configurações inválidas '{0}'. XmlElement inesperado: '{1}'. - - - - {0} (Data Row {1}) - {0} (Linha de Dados {1}) - - - - The ExpectedException attribute defined on test method {0}.{1} threw an exception during construction. -{2} - O atributo ExpectedException definido no método de teste {0}.{1} emitiu uma exceção durante a construção. -{2} - - - - Warning : A testsettings file or a vsmdi file is not supported with the MSTest V2 Adapter. - Aviso: um arquivo testsettings ou um arquivo vsmdi não tem suporte no MSTest V2 Adapter. - - - - TestContext Messages: - Mensagens TestContext: - - - - Error calling Test Cleanup method for test class {0}: {1} - Erro ao chamar o método Test Cleanup para a classe de teste {0}: {1} - - - - TestCleanup Stack Trace - Rastreamento de pilha TestCleanup - - - - [MSTest][Discovery][{0}] {1} - [MSTest][Descoberta][{0}] {1} - - - - Test Parallelization enabled for {0} (Workers: {1}, Scope: {2}) - Paralelização de Teste habilitada para {0} (Trabalhos: {1}, Escopo: {2}) - `Workers` is a setting name that shouldn't be localized. 'Scope' is a setting name that shouldn't be localized. - - - Invalid value '{0}' specified for 'Scope'. Supported scopes are {1}. - Valor inválido '{0}' especificado para 'Scope'. Os escopos compatíveis são {1}. - 'Scope' is a setting name that shouldn't be localized. - - - Invalid value '{0}' specified for 'Workers'. The value should be a non-negative integer. - Valor inválido '{0}' especificado para 'Workers'. O valor deve ser um inteiro não negativo. - `Workers` is a setting name that shouldn't be localized. - - - Failed to discover tests from assembly {0}. Reason:{1} - Falha ao descobrir testes por meio do assembly {0}. Motivo: {1} - - - - Test '{0}' was canceled - O '{0}' teste foi cancelado - - - - Invalid value '{0}' specified for 'ClassCleanupLifecycle'. Supported scopes are {1}. - Valor inválido '{0}' especificado para 'ClassCleanupLifecycle'. Os escopos suportados são {1}. - 'ClassCleanupLifecycle' is a setting name that shouldn't be localized. - - - Exception occurred while enumerating IDataSource attribute on "{0}.{1}": {2} - Ocorreu uma exceção ao enumerar o atributo IDataSource em "{0}.{1}": {2} - {0}: TypeName with namespace, -{1}: Method name, -{2}: Exception details - - - "{0}": (Failed to get exception description due to an exception of type "{1}". - "{0}": (Falha ao obter a descrição da exceção devido a uma exceção do tipo "{1}". - {0}: Type of the original exception that we're trying to get the description of. -{1}: Thrown exception - - - Exceptions thrown: - Exceções lançadas: - This is usually precedes by TestAssembly_AssemblyDiscoveryFailure message, and preceded by list of exceptions thrown in a test discovery session. - - - Getting custom attributes for type {0} threw exception (will ignore and use the reflection way): {1} - A obtenção de atributos personalizados para o tipo {0} gerou uma exceção (irá ignorar e usar o modo de reflexão): {1} - {0}: Attribute full type name. -{1}: Exception description - - - An older version of MSTestV2 package is loaded in assembly, test discovery might fail to discover all data tests if they depend on `.runsettings` file. - Uma versão mais antiga do pacote MSTestV2 é carregada no assembly, a descoberta de teste pode falhar ao descobrir todos os testes de dados se eles dependerem do arquivo `.runsettings`. - - - - The called code threw an exception that was caught, but the exception value was null - O código chamado lançou uma exceção que foi capturada, mas o valor da exceção era nulo - - - - Exception occurred while expanding IDataSource rows from attribute on "{0}.{1}": {2} - Ocorreu uma exceção ao expandir as linhas IDataSource do atributo em "{0}.{1}": {2} - {0}: TypeName with namespace, -{1}: Method name, -{2}: CannotExpandIDataSourceAttribute_DuplicateDisplayName or CannotExpandIDataSourceAttribute_CannotSerialize - - - Display name "{2}" on indexes {0} and {1} are duplicate. Display names should be unique. - O nome de exibição "{2}" nos índices {0} e {1} são duplicados. Os nomes de exibição devem ser exclusivos. - {0}, {1}: Zero based index if an element inside of an array -{2}: Test display name. - - - Data on index {0} for "{1}" cannot be serialized. All data provided through "IDataSource" should be serializable. If you need to test non-serializable data sources, please make sure you add "TestDataSourceDiscovery" attribute on your test assembly and set the discovery option to "DuringExecution". - Os dados no índice {0} para "{1}" não podem ser serializados. Todos os dados fornecidos por meio de "IDataSource" devem ser serializáveis. Se você precisar testar fontes de dados não serializáveis, certifique-se de adicionar o atributo "TestDataSourceDiscovery" em seu assembly de teste e defina a opção de descoberta como "DuringExecution". - {0}: Zero based index if an element inside of an array, -{1}: Test name - - - - \ No newline at end of file diff --git a/src/Adapter/MSTest.TestAdapter/Resources/xlf/Resource.ru.xlf b/src/Adapter/MSTest.TestAdapter/Resources/xlf/Resource.ru.xlf deleted file mode 100644 index 058011a770..0000000000 --- a/src/Adapter/MSTest.TestAdapter/Resources/xlf/Resource.ru.xlf +++ /dev/null @@ -1,482 +0,0 @@ - - - - - - Assembly cleanup method '{0}.{1}' timed out after {2}ms - Время ожидания метода очистки сборки "{0}.{1}" истекло через {2} мс - - - - Assembly cleanup method '{0}.{1}' was canceled - Метод очистки сборки "{0}.{1}" отменен - - - - Assembly initialize method '{0}.{1}' timed out after {2}ms - Время ожидания метода инициализации сборки "{0}.{1}" истекло через {2} мс - - - - Assembly initialize method '{0}.{1}' was canceled - Метод инициализации сборки "{0}.{1}" отменен - - - - Cannot run test method '{0}.{1}': Test data doesn't match method parameters. Either the count or types are different. -Test expected {2} parameter(s), with types '{3}', -but received {4} argument(s), with types '{5}'. - Не удается запустить метод теста "{0}.{1}": тестовые данные не соответствуют параметрам метода. Количество или типы отличаются. -Число ожидаемых для теста параметров: {2} с типами "{3}", -но число полученных аргументов: {4} с типами "{5}". - - - - Cannot run test method '{0}.{1}': Method has parameters, but does not define any test source. Use '[DataRow]', '[DynamicData]', or a custom 'ITestDataSource' data source to provide test data. - Не удается запустить метод теста "{0}.{1}": метод имеет параметры, но не определяет источник теста. Используйте "[DataRow]", "[DynamicData]" или настраиваемый источник данных "ITestDataSource" для предоставления тестовых данных. - - - - Class cleanup method '{0}.{1}' timed out after {2}ms - Время ожидания метода очистки класса "{0}.{1}" истекло через {2} мс - - - - Class cleanup method '{0}.{1}' was canceled - Метод очистки класса "{0}.{1}" отменен - - - - Class initialize method '{0}.{1}' timed out after {2}ms - Время ожидания метода инициализации класса "{0}.{1}" истекло через {2} мс - - - - Class initialize method '{0}.{1}' was canceled - Метод инициализации класса "{0}.{1}" отменен - - - - Both '.runsettings' and '.testconfig.json' files have been detected. Please select only one of these test configuration files. - Обнаружены файлы ".runsettings" и ".testconfig.json". Выберите только один из этих файлов тестовой конфигурации. - - - - Test '{0}' timed out after {1}ms - Время ожидания '{0}' истекло через {1}мс - - - - The type of the generic parameter '{0}' could not be inferred. - Не удалось определить тип универсального '{0}' параметра. - - - - The generic test method '{0}' doesn't have arguments, so the generic parameter cannot be inferred. - Универсальный метод '{0}' не имеет аргументов, поэтому невозможно вывести универсальный параметр. - - - - Found two conflicting types for generic parameter '{0}'. The conflicting types are '{1}' and '{2}'. - Обнаружены два конфликтующих типа для универсального параметра '{0}'. Конфликтуют типы '{1}' и '{2}'. - - - - Invalid value '{0}' for runsettings entry '{1}', setting will be ignored. - Недопустимое значение "{0}" для записи runsettings "{1}", параметр будет пропущен. - - - - Runsettings entry '<ExecutionApartmentState>STA</ExecutionApartmentState>' is not supported on non-Windows OSes. - Запись Runsettings "<ExecutionApartmentState>STA</ExecutionApartmentState>" не поддерживается в ОС, отличных от Windows. - - - - Running tests in any of the provided sources is not supported for the selected platform - Запуск тестов в любом из предоставленных источников не поддерживается на выбранной платформе - - - - Test cleanup method '{0}.{1}' timed out after {2}ms - Время ожидания метода очистки теста "{0}.{1}" истекло через {2} мс - - - - Test cleanup method '{0}.{1}' was canceled - Метод очистки теста "{0}.{1}" отменен - - - - Test initialize method '{0}.{1}' timed out after {2}ms - Время ожидания метода инициализации теста "{0}.{1}" истекло через {2} мс - - - - Test initialize method '{0}.{1}' was canceled - Метод инициализации теста "{0}.{1}" отменен - - - - TestCleanup method {0}.{1} threw exception. {2}. - Метод TestCleanup {0}.{1} создал исключение. {2}. - - - - --- End of inner exception stack trace --- - --- Конец трассировки стека внутренних исключений --- - - - - UTA014: {0}: Cannot define more than one method with the AssemblyCleanup attribute inside an assembly. - UTA014: {0}: в сборке невозможно определить несколько методов с атрибутом AssemblyCleanup. - - - - UTA013: {0}: Cannot define more than one method with the AssemblyInitialize attribute inside an assembly. - UTA013: {0}: в сборке невозможно определить несколько методов с атрибутом AssemblyInitialize. - - - - UTA026: {0}: Cannot define more than one method with the ClassCleanup attribute inside a class. - UTA026: {0}: в классе невозможно определить несколько методов с атрибутом ClassCleanup. - - - - UTA025: {0}: Cannot define more than one method with the ClassInitialize attribute inside a class. - UTA025: {0}: в классе невозможно определить несколько методов с атрибутом ClassInitialize. - - - - UTA024: {0}: Cannot define more than one method with the TestCleanup attribute. - UTA024: {0}: невозможно определить несколько методов с атрибутом TestCleanup. - - - - UTA018: {0}: Cannot define more than one method with the TestInitialize attribute. - UTA018: {0}: невозможно определить несколько методов с атрибутом TestInitialize. - - - - TestClass attribute defined on generic non-abstract class {0} - Атрибут TestClass, определенный в универсальном не абстрактном классе {0} - - - - Initialization method {0}.{1} threw exception. {2}. - Метод инициализации {0}.{1} вызвал исключение. {2}. - - - - Unable to create instance of class {0}. Error: {1}. - Не удалось создать экземпляр класса {0}. Ошибка: {1}. - - - - The test method '{0}.{1}' has multiple attributes derived from '{2}' defined on it. Only one such attribute is allowed. - У метода тестирования "{0}.{1}" есть несколько атрибутов, производных от заданного в нем "{2}". Допускается только один такой атрибут. - - - - Cannot find a valid constructor for test class '{0}'. Valid constructors are 'public' and either parameterless or with one parameter of type 'TestContext'. - Не удается найти допустимый конструктор для тестового класса "{0}". Допустимые конструкторы : "public" и конструкторы без параметров или с одним параметром типа "TestContext". - - - - Unable to set TestContext property for the class {0}. Error: {1}. - Не удалось задать свойство TestContext для класса {0}. Ошибка: {1}. - - - - (Failed to get the message for an exception of type {0} due to an exception.) - (Не удалось получить сообщение для исключения с типом {0} в связи с возникновением исключения.) - - - - UTA031: class {0} does not have valid TestContext property. TestContext must be of type TestContext, must be non-static, and must be public. For example: public TestContext TestContext. - UTA031: в классе {0} отсутствует допустимое свойство TestContext. Свойство TestContext должно относиться к типу TestContext, быть нестатическим и открытым. Например: public TestContext TestContext. - - - - UTA001: TestClass attribute defined on non-public class {0} - UTA001: атрибут TestClass определен в классе {0}, не являющемся открытым - - - - MSTestAdapter failed to discover tests in class '{0}' of assembly '{1}' because {2}. - Средству MSTestAdapter не удалось обнаружить тесты в классе "{0}" сборки "{1}", так как {2}. - - - - {0}: {1} - {0}: {1} - - - - Unable to load types from the test source '{0}'. Some or all of the tests in this source may not be discovered. -Error: {1} - Не удалось загрузить типы из тестового источника "{0}". В этом источнике могут быть не обнаружены некоторые или все тесты. -Ошибка: {1} - - - - File does not exist: {0} - Файл не существует: {0} - - - - UTA007: Method {1} defined in class {0} does not have correct signature. Test method marked with the [TestMethod] attribute must be non-static, public, return-type as void and should not take any parameter. Example: public void Test.Class1.Test(). Additionally, if you are using async-await in test method then return-type must be 'Task' or 'ValueTask'. Example: public async Task Test.Class1.Test2() - UTA007: метод {1}, определенный в классе {0}, имеет неправильную сигнатуру. Метод теста, помеченный атрибутом [TestMethod], должен быть нестатическим, открытым и иметь тип возвращаемого значения void; он также не должен принимать параметры. Пример: public void Test.Class1.Test(). Кроме того, при использовании async-await в методе теста возвращаемое значение должно иметь тип "Task" или "ValueTask". Пример: public async Task Test.Class1.Test2() - - - - TestContext cannot be Null. - TestContext не может иметь значение NULL. - - - - Assembly Cleanup method {0}.{1} failed. Error Message: {2}. StackTrace: {3} - Не удалось применить метод очистки сборки {0}.{1}. Сообщение об ошибке: {2}. Трассировка стека (StackTrace): {3}. - - - - Assembly Initialization method {0}.{1} threw exception. {2}: {3}. Aborting test execution. - Методом инициализации сборки {0}.{1} создано исключение. {2}: {3}. Выполнение теста прекращается. - - - - Class Cleanup method {0}.{1} failed. Error Message: {2}. Stack Trace: {3} - Не удалось применить метод очистки класса {0}.{1}. Сообщение об ошибке: {2}. Трассировка стека: {3}. - - - - Class Initialization method {0}.{1} threw exception. {2}: {3}. - Методом инициализации класса {0}.{1} создано исключение. {2}: {3}. - - - - An unhandled exception was thrown by the 'Execute' method. Please report this error to the author of the attribute '{0}'. -{1} - Необработанное исключение вызвано методом Execute. Сообщить об этой ошибке автору атрибута '{0}'. -{1} - - - - Error in executing test. No result returned by extension. If using extension of TestMethodAttribute then please contact vendor. - Ошибка при выполнении теста. Расширение не возвратило результаты. Если используется расширение атрибута TestMethodAttribute, обратитесь к поставщику. - - - - Method {0}.{1} has wrong signature. The method must be static, public, does not return a value and should not take any parameter. Additionally, if you are using async-await in method then return-type must be 'Task' or 'ValueTask'. - Метод {0}.{1} имеет неправильную сигнатуру. Метод должен быть статическим и открытым, не должен возвращать значение и принимать параметры. Кроме того, при использовании async-await в методе возвращаемое значение должно иметь тип "Task" или "ValueTask". - - - - Method {0}.{1} has wrong signature. The method must be static, public, does not return a value and should take a single parameter of type TestContext. Additionally, if you are using async-await in method then return-type must be 'Task' or 'ValueTask'. - Метод {0}.{1} имеет неправильную сигнатуру. Метод должен быть статическим и открытым, не должен возвращать значение и должен принимать один параметр, имеющий тип TestContext. Кроме того, при использовании async-await в методе возвращаемое значение должно иметь тип "Task" или "ValueTask". - - - - UTA054: {0}.{1} has invalid Timeout attribute. The timeout must be an integer value greater than 0. - UTA054: {0}. {1} имеет недопустимый атрибут времени ожидания. Значение времени ожидания должно быть положительным целым числом. - - - - UTA023: {0}: Cannot define predefined property {2} on method {1}. - UTA023: {0}: не удается определить предопределенное свойство {2} в методе {1}. - - - - UTA021: {0}: Null or empty custom property defined on method {1}. The custom property must have a valid name. - UTA021: {0}: в методе {1} определено пользовательское свойство, имя которого имеет значение NULL или пусто. Пользовательское свойство должно иметь допустимое имя. - - - - Method {0}.{1} does not exist. - Метод {0}.{1} не существует. - - - - Unable to find property {0}.TestContext. Error:{1}. - Не удается найти свойство {0}.TestContext. Ошибка: {1}. - - - - The {0}.TestContext has incorrect type. - Для свойства {0}.TestContext указан неправильный тип. - - - - Method {0}.{1} has wrong signature. The method must be non-static, public, does not return a value and should not take any parameter. Additionally, if you are using async-await in method then return-type must be 'Task' or 'ValueTask'. - Метод {0}.{1} имеет неправильную сигнатуру. Метод должен быть нестатическим и открытым, не должен возвращать значение и принимать параметры. Кроме того, при использовании async-await в методе возвращаемое значение должно иметь тип "Task" или "ValueTask". - - - - Unable to get type {0}. Error: {1}. - Не удается получить тип {0}. Ошибка: {1}. - - - - Test method {0} was not found. - Метод теста {0} не найден. - - - - Debug Trace: - Трассировка отладки: - - - - Failed to obtain the exception thrown by test method {0}.{1}. - Не удалось получить исключение, созданное методом теста {0}.{1}. - - - - Test method {0}.{1} threw exception: -{2} - Метод теста {0}.{1} создал исключение: -{2}. - - - - {0} For UWP projects, if you are using UI objects in test consider using [UITestMethod] attribute instead of [TestMethod] to execute test in UI thread. - {0} В проектах UWP, если в тесте используются объекты пользовательского интерфейса, рассмотрите возможность использования атрибута [UITestMethod] вместо атрибута [TestMethod] для выполнения теста в потоке пользовательского интерфейса. - - - - MSTestAdapterV2 - MSTestAdapterV2 - - - - Invalid settings '{0}'. Unexpected XmlAttribute: '{1}'. - Недопустимые параметры "{0}". Непредвиденный атрибут XmlAttribute: "{1}". - - - - Invalid settings '{0}'. Unexpected XmlElement: '{1}'. - Недопустимые параметры "{0}". Непредвиденный элемент XmlElement: "{1}". - - - - {0} (Data Row {1}) - {0} (строка данных {1}) - - - - The ExpectedException attribute defined on test method {0}.{1} threw an exception during construction. -{2} - Атрибут ExpectedException, определенный в методе теста {0}.{1}, породил исключение во время создания. -{2} - - - - Warning : A testsettings file or a vsmdi file is not supported with the MSTest V2 Adapter. - Внимание! Адаптер MSTest версии 2 не поддерживает файл TESTSETTINGS или VSMDI. - - - - TestContext Messages: - Сообщения TestContext: - - - - Error calling Test Cleanup method for test class {0}: {1} - Ошибка при вызове метода TestCleanup для тестового класса {0}: {1} - - - - TestCleanup Stack Trace - Трассировка стека TestCleanup - - - - [MSTest][Discovery][{0}] {1} - [MSTest][Discovery][{0}] {1} - - - - Test Parallelization enabled for {0} (Workers: {1}, Scope: {2}) - Включена параллелизация тестов для {0} (рабочие роли: {1}, область: {2}) - `Workers` is a setting name that shouldn't be localized. 'Scope' is a setting name that shouldn't be localized. - - - Invalid value '{0}' specified for 'Scope'. Supported scopes are {1}. - В поле "Область" указано недопустимое значение "{0}". Поддерживаемые области: {1}. - 'Scope' is a setting name that shouldn't be localized. - - - Invalid value '{0}' specified for 'Workers'. The value should be a non-negative integer. - В поле "Рабочие роли" указано недопустимое значение "{0}". Оно должно быть неотрицательным целым числом. - `Workers` is a setting name that shouldn't be localized. - - - Failed to discover tests from assembly {0}. Reason:{1} - Не удалось обнаружить тесты из сборки {0}. Причина:{1} - - - - Test '{0}' was canceled - Проверка '{0}' отменена - - - - Invalid value '{0}' specified for 'ClassCleanupLifecycle'. Supported scopes are {1}. - Для параметра "ClassCleanupLifecycle" указано недопустимое значение "{0}". Поддерживаемые области: {1}. - 'ClassCleanupLifecycle' is a setting name that shouldn't be localized. - - - Exception occurred while enumerating IDataSource attribute on "{0}.{1}": {2} - Возникло исключение при перечислении атрибута IDataSource в "{0}.{1}": {2} - {0}: TypeName with namespace, -{1}: Method name, -{2}: Exception details - - - "{0}": (Failed to get exception description due to an exception of type "{1}". - "{0}": (Не удалось получить описание исключения из-за исключения типа "{1}". - {0}: Type of the original exception that we're trying to get the description of. -{1}: Thrown exception - - - Exceptions thrown: - Выданные исключения: - This is usually precedes by TestAssembly_AssemblyDiscoveryFailure message, and preceded by list of exceptions thrown in a test discovery session. - - - Getting custom attributes for type {0} threw exception (will ignore and use the reflection way): {1} - При получении настраиваемых атрибутов для типа {0} возникло исключение (оно будет проигнорировано и будет использовано отражение): {1} - {0}: Attribute full type name. -{1}: Exception description - - - An older version of MSTestV2 package is loaded in assembly, test discovery might fail to discover all data tests if they depend on `.runsettings` file. - В сборку загружена старая версия пакета MSTestV2. При обнаружении тестов могут быть обнаружены не все тесты данных, если они зависят от файла ".runsettings". - - - - The called code threw an exception that was caught, but the exception value was null - Вызванный код вызвал исключение, которое было перехвачено, но значение исключения было равно NULL - - - - Exception occurred while expanding IDataSource rows from attribute on "{0}.{1}": {2} - Возникло исключение при развертывании строк IDataSource из атрибута "{0}.{1}": {2} - {0}: TypeName with namespace, -{1}: Method name, -{2}: CannotExpandIDataSourceAttribute_DuplicateDisplayName or CannotExpandIDataSourceAttribute_CannotSerialize - - - Display name "{2}" on indexes {0} and {1} are duplicate. Display names should be unique. - Отображаемые имена "{2}" в индексах {0} и {1} дублируются. Отображаемые имена должны быть уникальными. - {0}, {1}: Zero based index if an element inside of an array -{2}: Test display name. - - - Data on index {0} for "{1}" cannot be serialized. All data provided through "IDataSource" should be serializable. If you need to test non-serializable data sources, please make sure you add "TestDataSourceDiscovery" attribute on your test assembly and set the discovery option to "DuringExecution". - Не удается сериализовать данные в индексе {0} для "{1}". Все данные, предоставленные через IDataSource, должны быть сериализуемыми. Если необходимо протестировать несериализуемые источники данных, добавьте атрибут "TestDataSourceDiscovery" в тестовую сборку и установите для параметра обнаружения значение "DuringExecution". - {0}: Zero based index if an element inside of an array, -{1}: Test name - - - - \ No newline at end of file diff --git a/src/Adapter/MSTest.TestAdapter/Resources/xlf/Resource.tr.xlf b/src/Adapter/MSTest.TestAdapter/Resources/xlf/Resource.tr.xlf deleted file mode 100644 index 5713804864..0000000000 --- a/src/Adapter/MSTest.TestAdapter/Resources/xlf/Resource.tr.xlf +++ /dev/null @@ -1,482 +0,0 @@ - - - - - - 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ı - - - - Assembly cleanup method '{0}.{1}' was canceled - '{0}.{1}' bütünleştirilmiş kod temizleme yöntemi iptal edildi - - - - 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ı - - - - Assembly initialize method '{0}.{1}' was canceled - '{0}.{1}' derleme başlatma yöntemi iptal edildi - - - - Cannot run test method '{0}.{1}': Test data doesn't match method parameters. Either the count or types are different. -Test expected {2} parameter(s), with types '{3}', -but received {4} argument(s), with types '{5}'. - '{0}.{1}' test yöntemi çalıştırılamıyor: Test verileri yöntem parametreleriyle eşleşmiyor. Ya sayı ya da türler farklıdır. -Test '{3}' türüyle {2} parametre bekledi, -ancak, '{5}' türüyle {4} argüman aldı. - - - - Cannot run test method '{0}.{1}': Method has parameters, but does not define any test source. Use '[DataRow]', '[DynamicData]', or a custom 'ITestDataSource' data source to provide test data. - '{0}.{1}' test yöntemi çalıştırılamıyor: Yöntemin parametreleri var ancak herhangi bir test kaynağı tanımlamıyor. Test verilerini sağlamak için '[DataRow]', '[DynamicData]' veya özel bir 'ITestDataSource' veri kaynağı kullanın. - - - - 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ı - - - - Class cleanup method '{0}.{1}' was canceled - '{0}.{1}' sınıf temizleme yöntemi iptal edildi - - - - 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ı - - - - Class initialize method '{0}.{1}' was canceled - '{0}.{1}' sınıf başlatma yöntemi iptal edildi - - - - Both '.runsettings' and '.testconfig.json' files have been detected. Please select only one of these test configuration files. - Hem '.runsettings' hem de '.testconfig.json' dosyaları algılandı. Lütfen bu test yapılandırma dosyalarından yalnızca birini seçin. - - - - Test '{0}' timed out after {1}ms - Test '{0}' ms sonra zaman aşımına {1}oldu - - - - The type of the generic parameter '{0}' could not be inferred. - Genel parametre türü '{0}' çıkarsanamadı. - - - - The generic test method '{0}' doesn't have arguments, so the generic parameter cannot be inferred. - Genel test '{0}' bağımsız değişkene sahip olmadığından genel parametre çıkarsanamıyor. - - - - Found two conflicting types for generic parameter '{0}'. The conflicting types are '{1}' and '{2}'. - Genel parametre türü için iki çakışan tür '{0}'. Çakışan türler '{1}' '{2}'. - - - - Invalid value '{0}' for runsettings entry '{1}', setting will be ignored. - '{1}' çalıştırma ayarları girişi için '{0}' değeri geçersiz, ayar yoksayılacak. - - - - Runsettings entry '<ExecutionApartmentState>STA</ExecutionApartmentState>' is not supported on non-Windows OSes. - '<ExecutionApartmentState>STA</ExecutionApartmentState>' çalışma ayarları girişi Windows dışı işletim sistemlerinde desteklenmiyor. - - - - Running tests in any of the provided sources is not supported for the selected platform - Testlerin sağlanan kaynakların herhangi birinde çalıştırılması seçili platformda desteklenmiyor - - - - 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ı - - - - Test cleanup method '{0}.{1}' was canceled - '{0}.{1}' test temizleme yöntemi iptal edildi - - - - 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ı - - - - Test initialize method '{0}.{1}' was canceled - '{0}.{1}' test başlatma yöntemi iptal edildi - - - - TestCleanup method {0}.{1} threw exception. {2}. - TestCleanup metodu {0}.{1} özel durum oluşturdu. {2}. - - - - --- End of inner exception stack trace --- - --- İç özel durum yığın izlemesinin sonu --- - - - - UTA014: {0}: Cannot define more than one method with the AssemblyCleanup attribute inside an assembly. - UTA014: {0}: Bir bütünleştirilmiş kod içinde AssemblyCleanup özniteliği ile birden fazla metot tanımlanamaz. - - - - UTA013: {0}: Cannot define more than one method with the AssemblyInitialize attribute inside an assembly. - UTA013: {0}: Bir bütünleştirilmiş kod içinde AssemblyInitialize özniteliği ile birden fazla metot tanımlanamaz. - - - - UTA026: {0}: Cannot define more than one method with the ClassCleanup attribute inside a class. - UTA026: {0}: Bir sınıf içinde ClassCleanup özniteliği ile birden fazla metot tanımlanamaz. - - - - UTA025: {0}: Cannot define more than one method with the ClassInitialize attribute inside a class. - UTA025: {0}: Bir sınıf içinde ClassInitialize özniteliği ile birden fazla metot tanımlanamaz. - - - - UTA024: {0}: Cannot define more than one method with the TestCleanup attribute. - UTA024: {0}: TestCleanup özniteliği ile birden fazla metot tanımlanamaz. - - - - UTA018: {0}: Cannot define more than one method with the TestInitialize attribute. - UTA018: {0}: TestInitialize özniteliği ile birden fazla metot tanımlanamaz. - - - - TestClass attribute defined on generic non-abstract class {0} - {0} genel soyut olmayan sınıf üzerinde tanımlanan TestClass özniteliği - - - - Initialization method {0}.{1} threw exception. {2}. - Başlatma metodu {0}.{1} özel durum oluşturdu. {2}. - - - - Unable to create instance of class {0}. Error: {1}. - {0} sınıfının örneği oluşturulamıyor. Hata: {1}. - - - - The test method '{0}.{1}' has multiple attributes derived from '{2}' defined on it. Only one such attribute is allowed. - “{0}.{1}” test yöntemi, üzerinde tanımlanan “{2}” öğesinden türetilmiş birden fazla öznitelik içeriyor. Bu türde yalnızca bir tane özniteliğe izin verilir. - - - - Cannot find a valid constructor for test class '{0}'. Valid constructors are 'public' and either parameterless or with one parameter of type 'TestContext'. - '{0}' test sınıfı için geçerli bir oluşturucu bulunamıyor. Geçerli oluşturucular 'public' ve parametresiz veya 'TestContext' türünde tek bir parametre içeriyor. - - - - Unable to set TestContext property for the class {0}. Error: {1}. - {0} sınıfı için TestContext özelliği ayarlanamıyor. Hata: {1}. - - - - (Failed to get the message for an exception of type {0} due to an exception.) - (Bir özel durum nedeniyle, {0} türündeki özel durum iletisi alınamadı.) - - - - UTA031: class {0} does not have valid TestContext property. TestContext must be of type TestContext, must be non-static, and must be public. For example: public TestContext TestContext. - UTA031: {0}sınıfı geçerli bir TestContext özelliğine sahip değil. TestContext, TestContext türünde olmalı, static olmamalı ve public olmalıdır. Örnek: public TestContext TestContext. - - - - UTA001: TestClass attribute defined on non-public class {0} - UTA001: TestClass özniteliği genel olmayan {0} sınıfında tanımlanmış - - - - MSTestAdapter failed to discover tests in class '{0}' of assembly '{1}' because {2}. - MSTestAdapter, '{1}' bütünleştirilmiş kodunun '{0}' sınıfındaki testleri bulamadı. Nedeni: {2}. - - - - {0}: {1} - {0}: {1} - - - - Unable to load types from the test source '{0}'. Some or all of the tests in this source may not be discovered. -Error: {1} - '{0}' test kaynağından türler yüklenemiyor. Kaynaktaki testlerin bazıları veya tümü bulunamayabilir. -Hata: {1} - - - - File does not exist: {0} - Dosya yok: {0} - - - - UTA007: Method {1} defined in class {0} does not have correct signature. Test method marked with the [TestMethod] attribute must be non-static, public, return-type as void and should not take any parameter. Example: public void Test.Class1.Test(). Additionally, if you are using async-await in test method then return-type must be 'Task' or 'ValueTask'. Example: public async Task Test.Class1.Test2() - UTA007: {0} sınıfında tanımlanan {1} yönteminin imzası doğru değil. [TestMethod] özniteliğiyle işaretlenmiş test yöntemi statik olmayan, genel, dönüş türü void olan bir yöntem olmalıdır ve hiçbir parametre almamalıdır. Örnek: public void Test.Class1.Test(). Bunlara ek olarak, test metodunda async-await kullanıyorsanız return-type değeri 'Task' veya 'ValueTask' olmalıdır. Örnek: public async Task Test.Class1.Test2() - - - - TestContext cannot be Null. - TestContext, Null olamaz. - - - - Assembly Cleanup method {0}.{1} failed. Error Message: {2}. StackTrace: {3} - Bütünleştirilmiş Kod Temizleme metodu ({0}.{1}) başarısız oldu. Hata İletisi: {2}. StackTrace: {3} - - - - Assembly Initialization method {0}.{1} threw exception. {2}: {3}. Aborting test execution. - Bütünleştirilmiş Kod Başlatma metodu ({0}.{1}) özel durum oluşturdu. {2}: {3}. Test yürütmesi durduruluyor. - - - - Class Cleanup method {0}.{1} failed. Error Message: {2}. Stack Trace: {3} - Sınıf Temizleme metodu ({0}.{1}) başarısız oldu. Hata İletisi: {2}. Yığın İzlemesi: {3} - - - - Class Initialization method {0}.{1} threw exception. {2}: {3}. - Sınıf Başlatma metodu ({0}.{1}) özel durum oluşturdu. {2}: {3}. - - - - An unhandled exception was thrown by the 'Execute' method. Please report this error to the author of the attribute '{0}'. -{1} - 'Execute' metodu tarafından işlenmeyen bir özel durum oluşturuldu. Lütfen bu hatayı özniteliğin yazarına '{0}'. -{1} - - - - Error in executing test. No result returned by extension. If using extension of TestMethodAttribute then please contact vendor. - Test yürütülürken hata oluştu. Uzantı tarafından hiç sonuç döndürülmedi. TestMethodAttribute uzantısı kullanılıyorsa, lütfen satıcıya başvurun. - - - - Method {0}.{1} has wrong signature. The method must be static, public, does not return a value and should not take any parameter. Additionally, if you are using async-await in method then return-type must be 'Task' or 'ValueTask'. - {0}.{1} yönteminin imzası yanlış. Yöntem statik, genel, değer döndürmeyen bir yöntem olmalı, hiçbir parametre almamalıdır. Bunlara ek olarak, yöntemde async-await kullanıyorsanız return-type değeri 'Task' veya 'ValueTask' olmalıdır. - - - - Method {0}.{1} has wrong signature. The method must be static, public, does not return a value and should take a single parameter of type TestContext. Additionally, if you are using async-await in method then return-type must be 'Task' or 'ValueTask'. - {0}.{1} yönteminin imzası yanlış. Yöntem statik, genel, değer döndürmeyen bir yöntem olmalı ve TestContext türünde tek bir parametre almalıdır. Bunlara ek olarak, yöntemde async-await kullanıyorsanız return-type değeri 'Task' veya 'ValueTask' olmalıdır. - - - - UTA054: {0}.{1} has invalid Timeout attribute. The timeout must be an integer value greater than 0. - UTA054: {0}.{1} Timeout özniteliği geçersiz. Zaman aşımı değeri 0'dan büyük bir tamsayı olmalıdır. - - - - UTA023: {0}: Cannot define predefined property {2} on method {1}. - UTA023: {0}: Önceden tanımlanmış {2} özelliği {1} metodunda tanımlanamaz. - - - - UTA021: {0}: Null or empty custom property defined on method {1}. The custom property must have a valid name. - UTA021: {0}: {1} metodunda null veya boş özel özellik tanımlanmış. Özel özellik geçerli bir ada sahip olmalıdır. - - - - Method {0}.{1} does not exist. - {0}.{1} metodu yok. - - - - Unable to find property {0}.TestContext. Error:{1}. - {0}.TestContext özelliği bulunamıyor. Hata:{1}. - - - - The {0}.TestContext has incorrect type. - {0}.TestContext yanlış türe sahip. - - - - Method {0}.{1} has wrong signature. The method must be non-static, public, does not return a value and should not take any parameter. Additionally, if you are using async-await in method then return-type must be 'Task' or 'ValueTask'. - {0}.{1} yönteminin imzası yanlış. Yöntem statik olmayan, genel, değer döndürmeyen bir yöntem olmalı, hiçbir parametre almamalıdır. Bunlara ek olarak, yöntemde async-await kullanıyorsanız return-type değeri 'Task' veya 'ValueTask' olmalıdır. - - - - Unable to get type {0}. Error: {1}. - {0} türü alınamıyor. Hata: {1}. - - - - Test method {0} was not found. - {0} test metodu bulunamadı. - - - - Debug Trace: - Hata Ayıklama İzleyici: - - - - Failed to obtain the exception thrown by test method {0}.{1}. - {0}.{1} metodu tarafından oluşturulan özel durum alınamadı. - - - - Test method {0}.{1} threw exception: -{2} - {0}.{1} test metodu özel durum oluşturdu: -{2} - - - - {0} For UWP projects, if you are using UI objects in test consider using [UITestMethod] attribute instead of [TestMethod] to execute test in UI thread. - {0} UWP projeleri için testte UI nesneleri kullanıyorsanız, testi UI iş parçacığında yürütmek için [TestMethod] yerine [UITestMethod] özniteliğini kullanabilirsiniz. - - - - MSTestAdapterV2 - MSTestAdapterV2 - - - - Invalid settings '{0}'. Unexpected XmlAttribute: '{1}'. - Geçersiz '{0}' ayarları. Beklenmeyen XmlAttribute: '{1}'. - - - - Invalid settings '{0}'. Unexpected XmlElement: '{1}'. - Geçersiz '{0}' ayarları. Beklenmeyen XmlElement: '{1}'. - - - - {0} (Data Row {1}) - {0} (Veri Satırı {1}) - - - - The ExpectedException attribute defined on test method {0}.{1} threw an exception during construction. -{2} - {0}.{1} test yöntemi üzerinde tanımlanan ExpectedException özniteliği, oluşturma sırasında bir özel durum oluşturdu. -{2} - - - - Warning : A testsettings file or a vsmdi file is not supported with the MSTest V2 Adapter. - Uyarı : MSTest V2 Adapter ile bir testsettings dosyası veya bir vsmdi dosyası desteklenmez. - - - - TestContext Messages: - TestContext İletileri: - - - - Error calling Test Cleanup method for test class {0}: {1} - {0} test sınıfı için Test Temizleme metodu çağrılırken hata oluştu: {1} - - - - TestCleanup Stack Trace - TestCleanup Yığın İzleme - - - - [MSTest][Discovery][{0}] {1} - [MSTest][Discovery][{0}] {1} - - - - Test Parallelization enabled for {0} (Workers: {1}, Scope: {2}) - {0} için Test Paralelleştirme etkin (Çalışanlar: {1}, Kapsam: {2}). - `Workers` is a setting name that shouldn't be localized. 'Scope' is a setting name that shouldn't be localized. - - - Invalid value '{0}' specified for 'Scope'. Supported scopes are {1}. - 'Scope' için geçersiz '{0}' değeri belirtildi. Desteklenen kapsamlar {1}. - 'Scope' is a setting name that shouldn't be localized. - - - Invalid value '{0}' specified for 'Workers'. The value should be a non-negative integer. - 'Workers' için geçersiz '{0}' değeri belirtildi. Değer negatif olmayan bir tamsayı olmalıdır. - `Workers` is a setting name that shouldn't be localized. - - - Failed to discover tests from assembly {0}. Reason:{1} - {0} bütünleştirilmiş kodundan testler bulunamadı. Neden:{1} - - - - Test '{0}' was canceled - Test '{0}' iptal edildi - - - - Invalid value '{0}' specified for 'ClassCleanupLifecycle'. Supported scopes are {1}. - 'ClassCleanupLifecycle' için geçersiz '{0}' değeri belirtildi. Desteklenen kapsamlar {1}'dir. - 'ClassCleanupLifecycle' is a setting name that shouldn't be localized. - - - Exception occurred while enumerating IDataSource attribute on "{0}.{1}": {2} - "{0}.{1} " üzerinde IDataSource özniteliği numaralandırılırken özel durum oluştu: {2} - {0}: TypeName with namespace, -{1}: Method name, -{2}: Exception details - - - "{0}": (Failed to get exception description due to an exception of type "{1}". - "{0}": ("{1}" türündeki bir istisna nedeniyle özel durum açıklaması alınamadı. - {0}: Type of the original exception that we're trying to get the description of. -{1}: Thrown exception - - - Exceptions thrown: - Oluşturulan özel durumlar: - This is usually precedes by TestAssembly_AssemblyDiscoveryFailure message, and preceded by list of exceptions thrown in a test discovery session. - - - Getting custom attributes for type {0} threw exception (will ignore and use the reflection way): {1} - {0} tipi için özel niteliklerin alınması özel durum oluşturdu (yok sayar ve yansıma yolunu kullanır): {1} - {0}: Attribute full type name. -{1}: Exception description - - - An older version of MSTestV2 package is loaded in assembly, test discovery might fail to discover all data tests if they depend on `.runsettings` file. - Montaja MSTestV2 paketinin daha eski bir sürümü yüklenir, test keşfi, `.runsettings` dosyasına bağlılarsa tüm veri testlerini keşfetmede başarısız olabilir. - - - - The called code threw an exception that was caught, but the exception value was null - Çağrılan kod, yakalanan bir özel durum yarattı, ancak özel durum değeri boştu - - - - Exception occurred while expanding IDataSource rows from attribute on "{0}.{1}": {2} - "{0}. {1}" üzerindeki öznitelikten IDataSource satırları genişletilirken özel durum oluştu: {2} - {0}: TypeName with namespace, -{1}: Method name, -{2}: CannotExpandIDataSourceAttribute_DuplicateDisplayName or CannotExpandIDataSourceAttribute_CannotSerialize - - - Display name "{2}" on indexes {0} and {1} are duplicate. Display names should be unique. - {0} ve {1}dizinlerinde görünen ad " {2}" yineleniyor. Görünen adlar benzersiz olmalıdır. - {0}, {1}: Zero based index if an element inside of an array -{2}: Test display name. - - - Data on index {0} for "{1}" cannot be serialized. All data provided through "IDataSource" should be serializable. If you need to test non-serializable data sources, please make sure you add "TestDataSourceDiscovery" attribute on your test assembly and set the discovery option to "DuringExecution". - "{1}" için {0} indeksindeki veriler serileştirilemez. "IDataSource" aracılığıyla sağlanan tüm veriler serileştirilebilir olmalıdır. Serileştirilemeyen veri kaynaklarını test etmeniz gerekiyorsa, lütfen test derlemenize "TestDataSourceDiscovery" özniteliğini eklediğinizden ve keşif seçeneğini "DuringExecution" olarak ayarladığınızdan emin olun. - {0}: Zero based index if an element inside of an array, -{1}: Test name - - - - \ No newline at end of file diff --git a/src/Adapter/MSTest.TestAdapter/Resources/xlf/Resource.zh-Hans.xlf b/src/Adapter/MSTest.TestAdapter/Resources/xlf/Resource.zh-Hans.xlf deleted file mode 100644 index 0e0b87553b..0000000000 --- a/src/Adapter/MSTest.TestAdapter/Resources/xlf/Resource.zh-Hans.xlf +++ /dev/null @@ -1,482 +0,0 @@ - - - - - - Assembly cleanup method '{0}.{1}' timed out after {2}ms - 程序集清理方法“{0}.{1}”在 {2} ms 后超时 - - - - Assembly cleanup method '{0}.{1}' was canceled - 已取消程序集清理方法“{0}.{1}” - - - - Assembly initialize method '{0}.{1}' timed out after {2}ms - 程序集初始化方法“{0}.{1}”在 {2} ms 后超时 - - - - Assembly initialize method '{0}.{1}' was canceled - 已取消程序集初始化方法“{0}.{1}” - - - - Cannot run test method '{0}.{1}': Test data doesn't match method parameters. Either the count or types are different. -Test expected {2} parameter(s), with types '{3}', -but received {4} argument(s), with types '{5}'. - 无法运行测试方法“{0}.{1}”: 测试数据与方法参数不匹配。计数或类型不同。 -测试需要类型为“{3}”的 {2} 参数, -但收到了类型为“{5}”的 {4} 参数。 - - - - Cannot run test method '{0}.{1}': Method has parameters, but does not define any test source. Use '[DataRow]', '[DynamicData]', or a custom 'ITestDataSource' data source to provide test data. - 无法运行测试方法“{0}.{1}”: 方法具有参数,但未定义任何测试源。使用 “[DataRow]”、“[DynamicData]” 或自定义 “ITestDataSource” 数据源提供测试数据。 - - - - Class cleanup method '{0}.{1}' timed out after {2}ms - 类清理方法“{0}.{1}”在 {2} ms 后超时 - - - - Class cleanup method '{0}.{1}' was canceled - 已取消类清理方法“{0}.{1}” - - - - Class initialize method '{0}.{1}' timed out after {2}ms - 类初始化方法“{0}.{1}”在 {2} ms 后超时 - - - - Class initialize method '{0}.{1}' was canceled - 已取消类初始化方法“{0}.{1}” - - - - Both '.runsettings' and '.testconfig.json' files have been detected. Please select only one of these test configuration files. - 检测到 ".runsettings" 和 ".testconfig.json" 文件。请仅选择其中一个测试配置文件。 - - - - Test '{0}' timed out after {1}ms - 测试 '{0}' 在 {1}毫秒后超时 - - - - The type of the generic parameter '{0}' could not be inferred. - 无法推断 '{0}' 泛型参数的类型。 - - - - The generic test method '{0}' doesn't have arguments, so the generic parameter cannot be inferred. - 泛型测试方法 '{0}' 没有参数,因此无法推断泛型参数。 - - - - Found two conflicting types for generic parameter '{0}'. The conflicting types are '{1}' and '{2}'. - 发现泛型参数 '{0}' 的两个冲突类型。冲突类型 '{1}' 和 '{2}'。 - - - - Invalid value '{0}' for runsettings entry '{1}', setting will be ignored. - runsettings 项“{0}”的值“{1}”无效,将忽略设置。 - - - - Runsettings entry '<ExecutionApartmentState>STA</ExecutionApartmentState>' is not supported on non-Windows OSes. - 非 Windows 操作系统不支持 Runsettings 条目 "<ExecutionApartmentState>STA</ExecutionApartmentState>"。 - - - - Running tests in any of the provided sources is not supported for the selected platform - 选定的平台不支持在任何提供的源中运行测试 - - - - Test cleanup method '{0}.{1}' timed out after {2}ms - 测试清理方法“{0}.{1}”在 {2} ms 后超时 - - - - Test cleanup method '{0}.{1}' was canceled - 已取消测试清理方法“{0}.{1}” - - - - Test initialize method '{0}.{1}' timed out after {2}ms - 测试初始化方法“{0}.{1}”在 {2} ms 后超时 - - - - Test initialize method '{0}.{1}' was canceled - 已取消测试初始化方法“{0}.{1}” - - - - TestCleanup method {0}.{1} threw exception. {2}. - TestCleanup 方法 {0}.{1} 引发异常。{2}。 - - - - --- End of inner exception stack trace --- - ---内部异常堆栈跟踪结束--- - - - - UTA014: {0}: Cannot define more than one method with the AssemblyCleanup attribute inside an assembly. - UTA014: {0}: 在一个程序集内部,不能定义多个具有 AssemblyCleanup 特性的方法。 - - - - UTA013: {0}: Cannot define more than one method with the AssemblyInitialize attribute inside an assembly. - UTA013: {0}: 在一个程序集内部,不能定义多个具有 AssemblyInitialize 特性的方法。 - - - - UTA026: {0}: Cannot define more than one method with the ClassCleanup attribute inside a class. - UTA026: {0}: 在一个类内部,不能定义多个具有 ClassCleanup 特性的方法。 - - - - UTA025: {0}: Cannot define more than one method with the ClassInitialize attribute inside a class. - UTA025: {0}: 在一个类内部,不能定义多个具有 ClassInitialize 特性的方法。 - - - - UTA024: {0}: Cannot define more than one method with the TestCleanup attribute. - UTA024: {0}: 不能定义多个具有 TestCleanup 特性的方法。 - - - - UTA018: {0}: Cannot define more than one method with the TestInitialize attribute. - UTA018: {0}: 不能定义多个具有 TestInitialize 特性的方法。 - - - - TestClass attribute defined on generic non-abstract class {0} - 在泛型非抽象类 {0} 上定义的 TestClass 特性 - - - - Initialization method {0}.{1} threw exception. {2}. - 初始化方法 {0}.{1} 引发异常。{2}。 - - - - Unable to create instance of class {0}. Error: {1}. - 无法创建类 {0} 的实例。错误: {1}。 - - - - The test method '{0}.{1}' has multiple attributes derived from '{2}' defined on it. Only one such attribute is allowed. - 测试方法“{0}.{1}”具有多个在其上定义的“{2}”的派生属性。仅允许一个此类属性。 - - - - Cannot find a valid constructor for test class '{0}'. Valid constructors are 'public' and either parameterless or with one parameter of type 'TestContext'. - 找不到测试类“{0}”的有效构造函数。有效的构造函数为 “public”,但该构造函数无参数或具有一个类型为 “TestContext” 的参数。 - - - - Unable to set TestContext property for the class {0}. Error: {1}. - 无法设置类 {0} 的 TestContext 属性。错误: {1}。 - - - - (Failed to get the message for an exception of type {0} due to an exception.) - (因异常而未能获取类型为 {0} 的异常的消息。) - - - - UTA031: class {0} does not have valid TestContext property. TestContext must be of type TestContext, must be non-static, and must be public. For example: public TestContext TestContext. - UTA031: 类 {0} 没有有效的 TestContext 属性。TestContext 必须是 TestContext 类型并且必须是非静态和公共的。例如: public TestContext TestContext。 - - - - UTA001: TestClass attribute defined on non-public class {0} - UTA001: 在非公共类 {0} 上定义的 TestClass 特性 - - - - MSTestAdapter failed to discover tests in class '{0}' of assembly '{1}' because {2}. - 由于 {2},MSTestAdapter 未能在程序集“{1}”的类“{0}”中发现测试。 - - - - {0}: {1} - {0}: {1} - - - - Unable to load types from the test source '{0}'. Some or all of the tests in this source may not be discovered. -Error: {1} - 无法从测试源“{0}”加载类型。可能无法发现此源中的部分或所有测试。 -错误: {1} - - - - File does not exist: {0} - 文件不存在: {0} - - - - UTA007: Method {1} defined in class {0} does not have correct signature. Test method marked with the [TestMethod] attribute must be non-static, public, return-type as void and should not take any parameter. Example: public void Test.Class1.Test(). Additionally, if you are using async-await in test method then return-type must be 'Task' or 'ValueTask'. Example: public async Task Test.Class1.Test2() - UTA007: 在类 {0} 中定义的方法 {1} 没有正确的签名。用 [TestMethod] 特性标记的测试方法必须是返回类型为 void 的非静态的公共方法,并且不应采用任何参数。示例: public void Test.Class1.Test()。此外,如果在测试方法中使用同步等待,则返回类型必须为“Task”或“Value Task”。示例: public async Task Test.Class1.Test2() - - - - TestContext cannot be Null. - TestContext 不能为 NULL。 - - - - Assembly Cleanup method {0}.{1} failed. Error Message: {2}. StackTrace: {3} - 程序集清理方法 {0}.{1} 失败。错误消息: {2}。StackTrace: {3} - - - - Assembly Initialization method {0}.{1} threw exception. {2}: {3}. Aborting test execution. - 程序集初始化方法 {0}.{1} 引发异常。{2}: {3}。正在中止测试的执行。 - - - - Class Cleanup method {0}.{1} failed. Error Message: {2}. Stack Trace: {3} - 类清理方法 {0}.{1} 失败。错误消息: {2}。堆栈跟踪: {3} - - - - Class Initialization method {0}.{1} threw exception. {2}: {3}. - 类初始化方法 {0}.{1} 引发异常。{2}: {3}。 - - - - An unhandled exception was thrown by the 'Execute' method. Please report this error to the author of the attribute '{0}'. -{1} - “Execute”方法引发了未经处理的异常。请将此错误报告给属性 '{0}' 的作者。 -{1} - - - - Error in executing test. No result returned by extension. If using extension of TestMethodAttribute then please contact vendor. - 执行测试时出错。扩展未返回任何结果。如果使用的是扩展 TestMethodAttribute ,请与供应商联系。 - - - - Method {0}.{1} has wrong signature. The method must be static, public, does not return a value and should not take any parameter. Additionally, if you are using async-await in method then return-type must be 'Task' or 'ValueTask'. - 方法 {0}。{1}的签名错误。该方法必须是静态的公共方法、不返回值并且不应采用任何参数。此外,如果在方法中使用同步等待,则返回类型必须为“Task”或“Value Task”。 - - - - Method {0}.{1} has wrong signature. The method must be static, public, does not return a value and should take a single parameter of type TestContext. Additionally, if you are using async-await in method then return-type must be 'Task' or 'ValueTask'. - 方法 {0}。{1}的签名错误。该方法必须是静态的公共方法,不返回值,并且应采用一个 TestContext 类型的参数。此外,如果在方法中使用同步等待,则返回类型必须为“Task”或“Value Task”。 - - - - UTA054: {0}.{1} has invalid Timeout attribute. The timeout must be an integer value greater than 0. - UTA054: {0}。{1} 的超时属性无效。“超时”必须是大于零的整数值。 - - - - UTA023: {0}: Cannot define predefined property {2} on method {1}. - UTA023: {0}: 不能在方法 {1} 上定义预定义属性 {2}。 - - - - UTA021: {0}: Null or empty custom property defined on method {1}. The custom property must have a valid name. - UTA021: {0}: 对方法 {1} 定义了为 NULL 或为空的自定义属性。自定义属性必须具有有效名称。 - - - - Method {0}.{1} does not exist. - 方法 {0}.{1} 不存在。 - - - - Unable to find property {0}.TestContext. Error:{1}. - 无法找到属性 {0}.TestContext。错误: {1}。 - - - - The {0}.TestContext has incorrect type. - {0}.TestContext 的类型不正确。 - - - - Method {0}.{1} has wrong signature. The method must be non-static, public, does not return a value and should not take any parameter. Additionally, if you are using async-await in method then return-type must be 'Task' or 'ValueTask'. - 方法 {0}。{1}的签名错误。该方法必须是非静态的公共方法、不返回值并且不应采用任何参数。此外,如果在方法中使用同步等待,则返回类型必须为“Task”或“Value Task”。 - - - - Unable to get type {0}. Error: {1}. - 无法获取类型 {0}。错误: {1}。 - - - - Test method {0} was not found. - 未找到测试方法 {0}。 - - - - Debug Trace: - 调试跟踪: - - - - Failed to obtain the exception thrown by test method {0}.{1}. - 未能获取测试方法 {0}.{1} 引发的异常。 - - - - Test method {0}.{1} threw exception: -{2} - 测试方法 {0}.{1} 引发了异常: -{2} - - - - {0} For UWP projects, if you are using UI objects in test consider using [UITestMethod] attribute instead of [TestMethod] to execute test in UI thread. - {0} 对于 UWP 项目,如果在测试中使用 UI 对象,请考虑使用 [UITestMethod] 属性代替 [TestMethod] 属性在 UI 线程中执行测试。 - - - - MSTestAdapterV2 - MSTestAdapterV2 - - - - Invalid settings '{0}'. Unexpected XmlAttribute: '{1}'. - 设置“{0}”无效。意外的 XmlAttribute:“{1}”。 - - - - Invalid settings '{0}'. Unexpected XmlElement: '{1}'. - 设置“{0}”无效。意外的 XmlElement:“{1}”。 - - - - {0} (Data Row {1}) - {0} (数据行 {1}) - - - - The ExpectedException attribute defined on test method {0}.{1} threw an exception during construction. -{2} - 测试方法 {0}.{1} 上定义的 ExpectedException 属性在构造过程中引发了一个异常。 -{2} - - - - Warning : A testsettings file or a vsmdi file is not supported with the MSTest V2 Adapter. - 警告: MSTest V2 适配器不支持 testsettings 文件或 vsmdi 文件。 - - - - TestContext Messages: - TestContext 消息: - - - - Error calling Test Cleanup method for test class {0}: {1} - 为测试类 {0} 调用 Test Cleanup 方法时出错: {1} - - - - TestCleanup Stack Trace - TestCleanup 堆栈跟踪 - - - - [MSTest][Discovery][{0}] {1} - [MSTest][发现][{0}] {1} - - - - Test Parallelization enabled for {0} (Workers: {1}, Scope: {2}) - 已为 {0} 启用测试并行化(工作线程: {1},范围: {2}) - `Workers` is a setting name that shouldn't be localized. 'Scope' is a setting name that shouldn't be localized. - - - Invalid value '{0}' specified for 'Scope'. Supported scopes are {1}. - 为“范围”指定的值“{0}”无效。受支持的范围为 {1}。 - 'Scope' is a setting name that shouldn't be localized. - - - Invalid value '{0}' specified for 'Workers'. The value should be a non-negative integer. - 为“辅助角色”指定的值“{0}”无效。该值应为非负整数。 - `Workers` is a setting name that shouldn't be localized. - - - Failed to discover tests from assembly {0}. Reason:{1} - 未能发现程序集 {0} 中的测试。原因: {1} - - - - Test '{0}' was canceled - 测试 '{0}' 已取消 - - - - Invalid value '{0}' specified for 'ClassCleanupLifecycle'. Supported scopes are {1}. - 为“ClassCleanupLifecycle”指定的值“{0}”无效。支持的作用域为 {1}。 - 'ClassCleanupLifecycle' is a setting name that shouldn't be localized. - - - Exception occurred while enumerating IDataSource attribute on "{0}.{1}": {2} - 枚举 {0} 上的 IDataSource 属性时发生异常。{1}": {2} - {0}: TypeName with namespace, -{1}: Method name, -{2}: Exception details - - - "{0}": (Failed to get exception description due to an exception of type "{1}". - “{0}”:(由于类型“{1}”异常,无法获取异常说明。 - {0}: Type of the original exception that we're trying to get the description of. -{1}: Thrown exception - - - Exceptions thrown: - 引发的异常: - This is usually precedes by TestAssembly_AssemblyDiscoveryFailure message, and preceded by list of exceptions thrown in a test discovery session. - - - Getting custom attributes for type {0} threw exception (will ignore and use the reflection way): {1} - 获取类型 {0} 自定义属性引发异常(将忽略并使用反射方式): {1} - {0}: Attribute full type name. -{1}: Exception description - - - An older version of MSTestV2 package is loaded in assembly, test discovery might fail to discover all data tests if they depend on `.runsettings` file. - 程序集中加载了 MSTestV2 包的较旧版本,如果测试发现依赖于“.runsettings”文件,则它们可能无法发现所有数据测试。 - - - - The called code threw an exception that was caught, but the exception value was null - 调用的代码引发了捕获的异常,但异常值为 null - - - - Exception occurred while expanding IDataSource rows from attribute on "{0}.{1}": {2} - 从“{0}.{1}”上的属性扩展 IDataSource 行时出现异常: {2} - {0}: TypeName with namespace, -{1}: Method name, -{2}: CannotExpandIDataSourceAttribute_DuplicateDisplayName or CannotExpandIDataSourceAttribute_CannotSerialize - - - Display name "{2}" on indexes {0} and {1} are duplicate. Display names should be unique. - 索引 {0} 和 {1} 上的显示名称“{2}”重复。显示名称应是唯一的。 - {0}, {1}: Zero based index if an element inside of an array -{2}: Test display name. - - - Data on index {0} for "{1}" cannot be serialized. All data provided through "IDataSource" should be serializable. If you need to test non-serializable data sources, please make sure you add "TestDataSourceDiscovery" attribute on your test assembly and set the discovery option to "DuringExecution". - 无法序列化“{1}”的索引 {0} 上的数据。通过“IDataSource”提供的所有数据都应可序列化。如果需要测试不可序列化的数据源,请确保在测试程序集上添加“TestDataSourceDiscovery”属性,并将发现选项设置为“DuringExecution”。 - {0}: Zero based index if an element inside of an array, -{1}: Test name - - - - \ No newline at end of file diff --git a/src/Adapter/MSTest.TestAdapter/Resources/xlf/Resource.zh-Hant.xlf b/src/Adapter/MSTest.TestAdapter/Resources/xlf/Resource.zh-Hant.xlf deleted file mode 100644 index d247e040da..0000000000 --- a/src/Adapter/MSTest.TestAdapter/Resources/xlf/Resource.zh-Hant.xlf +++ /dev/null @@ -1,482 +0,0 @@ - - - - - - Assembly cleanup method '{0}.{1}' timed out after {2}ms - 組件清理方法 '{0}.{1}' 在 {2} 毫秒後已逾時 - - - - Assembly cleanup method '{0}.{1}' was canceled - 已取消組件清理方法 '{0}.{1}' - - - - Assembly initialize method '{0}.{1}' timed out after {2}ms - 組件初始化方法 '{0}.{1}' 在 {2} 毫秒後已逾時 - - - - Assembly initialize method '{0}.{1}' was canceled - 已取消組件初始化方法 '{0}.{1}' - - - - Cannot run test method '{0}.{1}': Test data doesn't match method parameters. Either the count or types are different. -Test expected {2} parameter(s), with types '{3}', -but received {4} argument(s), with types '{5}'. - 無法執行測試方法 '{0}.{1}': 測試資料不符合方法參數。計數或類型不同。 -測試預期的 {2} 參數,類型為 '{3}', -但收到 {4} 引數,類型為 '{5}'。 - - - - Cannot run test method '{0}.{1}': Method has parameters, but does not define any test source. Use '[DataRow]', '[DynamicData]', or a custom 'ITestDataSource' data source to provide test data. - 無法執行測試方法 '{0}.{1}': 方法具有參數,但未定義任何測試來源。使用 '[DataRow]'、'[DynamicData]' 或自訂 'ITestDataSource' 資料來源來提供測試資料。 - - - - Class cleanup method '{0}.{1}' timed out after {2}ms - 類別清理方法 '{0}.{1}' 在 {2} 毫秒後已逾時 - - - - Class cleanup method '{0}.{1}' was canceled - 已取消類別清理方法 '{0}.{1}' - - - - Class initialize method '{0}.{1}' timed out after {2}ms - 類別初始化方法 '{0}.{1}' 在 {2} 毫秒後已逾時 - - - - Class initialize method '{0}.{1}' was canceled - 已取消類別初始化方法 '{0}.{1}' - - - - Both '.runsettings' and '.testconfig.json' files have been detected. Please select only one of these test configuration files. - 偵測到 '.runsettings' 和 '.testconfig.json' 檔案。請只選取其中一個測試設定檔。 - - - - Test '{0}' timed out after {1}ms - 測試 '{0}' 在 {1}毫秒後逾時 - - - - The type of the generic parameter '{0}' could not be inferred. - 無法推斷泛型參數 '{0}' 的類型。 - - - - The generic test method '{0}' doesn't have arguments, so the generic parameter cannot be inferred. - 泛型測試方法 '{0}' 沒有自變數,因此無法推斷泛型參數。 - - - - Found two conflicting types for generic parameter '{0}'. The conflicting types are '{1}' and '{2}'. - 發現兩個衝突的泛型參數類型 '{0}'。衝突的類型 '{1}' 且 '{2}'。 - - - - Invalid value '{0}' for runsettings entry '{1}', setting will be ignored. - runsettings 項目 '{1}' 的值 '{0}' 無效,將忽略設定。 - - - - Runsettings entry '<ExecutionApartmentState>STA</ExecutionApartmentState>' is not supported on non-Windows OSes. - 非 Windows OS 不支援 Runsettings 項目 '<ExecutionApartmentState>STA</ExecutionApartmentState>'。 - - - - Running tests in any of the provided sources is not supported for the selected platform - 所選取的平台不支援在任何提供的來源中執行測試 - - - - Test cleanup method '{0}.{1}' timed out after {2}ms - 測試清理方法 '{0}.{1}' 在 {2} 毫秒後已逾時 - - - - Test cleanup method '{0}.{1}' was canceled - 已取消測試清理方法 '{0}.{1}' - - - - Test initialize method '{0}.{1}' timed out after {2}ms - 測試初始化方法 '{0}.{1}' 在 {2} 毫秒後已逾時 - - - - Test initialize method '{0}.{1}' was canceled - 已取消測試初始化方法 '{0}.{1}' - - - - TestCleanup method {0}.{1} threw exception. {2}. - TestCleanup 方法 {0}.{1} 擲回例外狀況。{2}。 - - - - --- End of inner exception stack trace --- - --- 已到達內部例外狀況堆疊追蹤的結尾 --- - - - - UTA014: {0}: Cannot define more than one method with the AssemblyCleanup attribute inside an assembly. - UTA014: {0}: 組件內不可定義一個以上具有 AssemblyCleanup 屬性的方法。 - - - - UTA013: {0}: Cannot define more than one method with the AssemblyInitialize attribute inside an assembly. - UTA013: {0}: 組件內不可定義一個以上具有 AssemblyInitialize 屬性的方法。 - - - - UTA026: {0}: Cannot define more than one method with the ClassCleanup attribute inside a class. - UTA026: {0}: 類別內不可定義一個以上具有 ClassCleanup 屬性的方法。 - - - - UTA025: {0}: Cannot define more than one method with the ClassInitialize attribute inside a class. - UTA025: {0}: 類別內不可定義一個以上具有 ClassInitialize 屬性的方法。 - - - - UTA024: {0}: Cannot define more than one method with the TestCleanup attribute. - UTA024: {0}: 不可定義一個以上具有 TestCleanup 屬性的方法。 - - - - UTA018: {0}: Cannot define more than one method with the TestInitialize attribute. - UTA018: {0}: 不可定義一個以上具有 TestInitialize 屬性的方法。 - - - - TestClass attribute defined on generic non-abstract class {0} - 在一般非抽象類別上定義的 TestClass 屬性 {0} - - - - Initialization method {0}.{1} threw exception. {2}. - 初始設定方法 {0}.{1} 擲回例外狀況。{2}。 - - - - Unable to create instance of class {0}. Error: {1}. - 無法建立類別 {0} 的執行個體。錯誤: {1}。 - - - - The test method '{0}.{1}' has multiple attributes derived from '{2}' defined on it. Only one such attribute is allowed. - 測試方法 '{0}.{1}' 具有多個衍生自 '{2}' 的屬性根據其定義。只允許一個此類屬性。 - - - - Cannot find a valid constructor for test class '{0}'. Valid constructors are 'public' and either parameterless or with one parameter of type 'TestContext'. - 找不到測試類別 '{0}' 的有效建構函式。有效的建構函式為 'public' 且無參數或具有一個類型為 'TestContext' 的參數。 - - - - Unable to set TestContext property for the class {0}. Error: {1}. - 無法設定類別 {0} 的 TestContext 屬性。錯誤: {1}。 - - - - (Failed to get the message for an exception of type {0} due to an exception.) - (因為發生例外狀況,所以無法取得類型 {0} 之例外狀況的訊息。) - - - - UTA031: class {0} does not have valid TestContext property. TestContext must be of type TestContext, must be non-static, and must be public. For example: public TestContext TestContext. - UTA031: 類別 {0}不具備有效的 TestContext 屬性。TestContext 必須是 TestContext 類型、必須是非靜態的,而且必須是公用的。例如: public TestContext TestContext。 - - - - UTA001: TestClass attribute defined on non-public class {0} - UTA001: 在非公用類別 {0} 上定義了 TestClass 屬性 - - - - MSTestAdapter failed to discover tests in class '{0}' of assembly '{1}' because {2}. - MSTestAdapter 無法在組件 '{1}' 的類別 '{0}' 中探索測試,因為 {2}。 - - - - {0}: {1} - {0}: {1} - - - - Unable to load types from the test source '{0}'. Some or all of the tests in this source may not be discovered. -Error: {1} - 無法從測試來源 '{0}' 載入類型。有可能無法探索此來源中的部分或所有測試。 -錯誤: {1} - - - - File does not exist: {0} - 檔案不存在: {0} - - - - UTA007: Method {1} defined in class {0} does not have correct signature. Test method marked with the [TestMethod] attribute must be non-static, public, return-type as void and should not take any parameter. Example: public void Test.Class1.Test(). Additionally, if you are using async-await in test method then return-type must be 'Task' or 'ValueTask'. Example: public async Task Test.Class1.Test2() - UTA007: 類別 {0} 中定義的方法 {1} 沒有正確的簽章。標記 [TestMethod] 屬性的測試方法必須為非靜態、公用、傳回類型為 void,而且不應該接受任何參數。範例: public void Test.Class1.Test()。此外,如果您在測試方法中使用 async-await,則傳回類型必須是 'Task' 或 'ValueTask'。範例: public async Task Test.Class1.Test2() - - - - TestContext cannot be Null. - TestContext 不可為 Null。 - - - - Assembly Cleanup method {0}.{1} failed. Error Message: {2}. StackTrace: {3} - 組件清除方法 {0}.{1} 失敗。錯誤訊息: {2}。堆疊追蹤: {3} - - - - Assembly Initialization method {0}.{1} threw exception. {2}: {3}. Aborting test execution. - 組件初始設定方法 {0}.{1} 擲回例外狀況。{2}: {3}。正在中止測試執行。 - - - - Class Cleanup method {0}.{1} failed. Error Message: {2}. Stack Trace: {3} - 類別清除方法 {0}.{1} 失敗。錯誤訊息: {2}。堆疊追蹤: {3} - - - - Class Initialization method {0}.{1} threw exception. {2}: {3}. - 類別初始設定方法 {0}.{1} 擲回例外狀況。{2}: {3}。 - - - - An unhandled exception was thrown by the 'Execute' method. Please report this error to the author of the attribute '{0}'. -{1} - 'Execute' 方法擲回未處理的例外狀況。請將此錯誤回報給屬性 '{0}' 的作者。 -{1} - - - - Error in executing test. No result returned by extension. If using extension of TestMethodAttribute then please contact vendor. - 執行測試時發生錯誤。擴充功能未傳回任何結果。如果您使用 TestMethodAttribute 的擴充功能,請連絡廠商。 - - - - Method {0}.{1} has wrong signature. The method must be static, public, does not return a value and should not take any parameter. Additionally, if you are using async-await in method then return-type must be 'Task' or 'ValueTask'. - 方法 {0}.{1} 有錯誤的簽章。方法必須為靜態、公用、不傳回值,並且不應該接受任何參數。此外,如果您在方法中使用 async-await,則傳回類型必須是 'Task' 或 'ValueTask'。 - - - - Method {0}.{1} has wrong signature. The method must be static, public, does not return a value and should take a single parameter of type TestContext. Additionally, if you are using async-await in method then return-type must be 'Task' or 'ValueTask'. - 方法 {0}.{1} 有錯誤的簽章。方法必須為靜態、公用、不傳回值,並且應接受類型為 TestContext 的單一參數。此外,如果您在方法中使用 async-await,則傳回類型必須是 'Task' 或 'ValueTask'。 - - - - UTA054: {0}.{1} has invalid Timeout attribute. The timeout must be an integer value greater than 0. - UTA054: {0}。{1} 中具有無效的 Timeout 屬性。逾時必須為大於 0 的整數值。 - - - - UTA023: {0}: Cannot define predefined property {2} on method {1}. - UTA023: {0}: 不可在方法 {1} 上定義預先定義的屬性 {2}。 - - - - UTA021: {0}: Null or empty custom property defined on method {1}. The custom property must have a valid name. - UTA021: {0}: 在方法 {1} 上定義了 Null 或空白的自訂屬性。自訂屬性的名稱必須有效。 - - - - Method {0}.{1} does not exist. - 方法 {0}.{1} 不存在。 - - - - Unable to find property {0}.TestContext. Error:{1}. - 找不到屬性 {0}.TestContext。錯誤: {1}。 - - - - The {0}.TestContext has incorrect type. - {0}.TestContext 有不正確的類型。 - - - - Method {0}.{1} has wrong signature. The method must be non-static, public, does not return a value and should not take any parameter. Additionally, if you are using async-await in method then return-type must be 'Task' or 'ValueTask'. - 方法 {0}.{1} 有錯誤的簽章。方法必須為非靜態、公用、不傳回值,並且不應該接受任何參數。此外,如果您在方法中使用 async-await,則傳回類型必須是 'Task' 或 'ValueTask'。 - - - - Unable to get type {0}. Error: {1}. - 無法取得類型 {0}。錯誤: {1}。 - - - - Test method {0} was not found. - 找不到測試方法 {0}。 - - - - Debug Trace: - 偵錯追蹤: - - - - Failed to obtain the exception thrown by test method {0}.{1}. - 無法取得測試方法 {0}.{1} 所擲回的例外狀況。 - - - - Test method {0}.{1} threw exception: -{2} - 測試方法 {0}.{1} 擲回例外狀況: -{2} - - - - {0} For UWP projects, if you are using UI objects in test consider using [UITestMethod] attribute instead of [TestMethod] to execute test in UI thread. - {0} 針對 UWP 專案,如果您在測試中使用 UI 物件,請考慮使用 [UITestMethod] 屬性取代 [TestMethod] 在 UI 執行緒中執行測試。 - - - - MSTestAdapterV2 - MSTestAdapterV2 - - - - Invalid settings '{0}'. Unexpected XmlAttribute: '{1}'. - 設定 '{0}' 無效。未預期的 XmlAttribute: '{1}'。 - - - - Invalid settings '{0}'. Unexpected XmlElement: '{1}'. - 設定 '{0}' 無效。未預期的 XmlElement: '{1}'。 - - - - {0} (Data Row {1}) - {0} (資料列 {1}) - - - - The ExpectedException attribute defined on test method {0}.{1} threw an exception during construction. -{2} - 測試方法 {0} 上定義的 ExpectedException 屬性。{1} 在建構期間擲回例外狀況。 -{2} - - - - Warning : A testsettings file or a vsmdi file is not supported with the MSTest V2 Adapter. - 警告: MSTest V2 配接器不支援 testsettings 檔案 vsmdi 檔案。 - - - - TestContext Messages: - TestContext 訊息: - - - - Error calling Test Cleanup method for test class {0}: {1} - 呼叫測試類別 {0} 的測試清除方法時發生錯誤: {1} - - - - TestCleanup Stack Trace - TestCleanup 堆疊追蹤 - - - - [MSTest][Discovery][{0}] {1} - [MSTest][Discovery][{0}] {1} - - - - Test Parallelization enabled for {0} (Workers: {1}, Scope: {2}) - 已為 {0} 啟用平行測試 (背景工作角色: {1},範圍: {2}) - `Workers` is a setting name that shouldn't be localized. 'Scope' is a setting name that shouldn't be localized. - - - Invalid value '{0}' specified for 'Scope'. Supported scopes are {1}. - 為 'Scope' 指定的值 '{0}' 無效。支援的範圍為 {1}。 - 'Scope' is a setting name that shouldn't be localized. - - - Invalid value '{0}' specified for 'Workers'. The value should be a non-negative integer. - 為 'Workers' 的值 '{0}' 無效。值應為非負整數。 - `Workers` is a setting name that shouldn't be localized. - - - Failed to discover tests from assembly {0}. Reason:{1} - 無法從組件 {0} 中探索測試。原因:{1} - - - - Test '{0}' was canceled - 已取消測試 '{0}' - - - - Invalid value '{0}' specified for 'ClassCleanupLifecycle'. Supported scopes are {1}. - 為 'ClassCleanupLifecycle' 指定的值 '{0}' 無效。支援的範圍為 {1}。 - 'ClassCleanupLifecycle' is a setting name that shouldn't be localized. - - - Exception occurred while enumerating IDataSource attribute on "{0}.{1}": {2} - 列舉「{0}.{1}」上的 IDataSource 屬性時發生例外狀況: {2} - {0}: TypeName with namespace, -{1}: Method name, -{2}: Exception details - - - "{0}": (Failed to get exception description due to an exception of type "{1}". - 「{0}」: (因為類型「{1}」的例外狀況而無法取得例外狀況描述。 - {0}: Type of the original exception that we're trying to get the description of. -{1}: Thrown exception - - - Exceptions thrown: - 擲回的例外狀況數: - This is usually precedes by TestAssembly_AssemblyDiscoveryFailure message, and preceded by list of exceptions thrown in a test discovery session. - - - Getting custom attributes for type {0} threw exception (will ignore and use the reflection way): {1} - 取得類型 {0} 擲回例外狀況的自訂屬性 (將會略過並使用反映方式): {1} - {0}: Attribute full type name. -{1}: Exception description - - - An older version of MSTestV2 package is loaded in assembly, test discovery might fail to discover all data tests if they depend on `.runsettings` file. - 元件中已載入舊版的 MSTestV2 套件,如果測試探索相依於 '.runsettings' 檔案,則測試探索可能無法探索所有資料測試。 - - - - The called code threw an exception that was caught, but the exception value was null - 被呼叫的程式碼擲回攔截到的例外狀況,但例外狀況值為 Null - - - - Exception occurred while expanding IDataSource rows from attribute on "{0}.{1}": {2} - 從「{0}.{1}」上的屬性展開 IDataSource 資料列時發生例外狀況: {2} - {0}: TypeName with namespace, -{1}: Method name, -{2}: CannotExpandIDataSourceAttribute_DuplicateDisplayName or CannotExpandIDataSourceAttribute_CannotSerialize - - - Display name "{2}" on indexes {0} and {1} are duplicate. Display names should be unique. - 索引 {0} 和 {1} 上的顯示名稱「{2}」重複。顯示名稱必須是唯一的。 - {0}, {1}: Zero based index if an element inside of an array -{2}: Test display name. - - - Data on index {0} for "{1}" cannot be serialized. All data provided through "IDataSource" should be serializable. If you need to test non-serializable data sources, please make sure you add "TestDataSourceDiscovery" attribute on your test assembly and set the discovery option to "DuringExecution". - 無法序列化「{1}"」索引 {0} 上的資料。透過「IDataSource」提供的所有資料應可序列化。如果您需要測試不可序列化的資料來源,請務必在測試元件上新增「TestDataSourceDiscovery」屬性,並將探索選項設定為「DuringExecution」。 - {0}: Zero based index if an element inside of an array, -{1}: Test name - - - - \ No newline at end of file diff --git a/src/Adapter/MSTest.TestAdapter/TestingPlatformAdapter/BridgedTraceLogger.cs b/src/Adapter/MSTest.TestAdapter/TestingPlatformAdapter/BridgedTraceLogger.cs index e5332169f0..86cdf28df1 100644 --- a/src/Adapter/MSTest.TestAdapter/TestingPlatformAdapter/BridgedTraceLogger.cs +++ b/src/Adapter/MSTest.TestAdapter/TestingPlatformAdapter/BridgedTraceLogger.cs @@ -16,12 +16,27 @@ public BridgedTraceLogger(ILogger logger) => _logger = logger; public void LogError(string format, params object?[] args) - => _logger.LogError(string.Format(CultureInfo.CurrentCulture, format, args)); + { + if (_logger.IsEnabled(LogLevel.Error)) + { + _logger.LogError(string.Format(CultureInfo.CurrentCulture, format, args)); + } + } public void LogInfo(string format, params object?[] args) - => _logger.LogInformation(string.Format(CultureInfo.CurrentCulture, format, args)); + { + if (_logger.IsEnabled(LogLevel.Information)) + { + _logger.LogInformation(string.Format(CultureInfo.CurrentCulture, format, args)); + } + } public void LogWarning(string format, params object?[] args) - => _logger.LogWarning(string.Format(CultureInfo.CurrentCulture, format, args)); + { + if (_logger.IsEnabled(LogLevel.Warning)) + { + _logger.LogWarning(string.Format(CultureInfo.CurrentCulture, format, args)); + } + } } #endif diff --git a/src/Adapter/MSTest.TestAdapter/TestingPlatformAdapter/MSTestBannerCapability.cs b/src/Adapter/MSTest.TestAdapter/TestingPlatformAdapter/MSTestBannerCapability.cs index 3ebc32ee6d..f00335287c 100644 --- a/src/Adapter/MSTest.TestAdapter/TestingPlatformAdapter/MSTestBannerCapability.cs +++ b/src/Adapter/MSTest.TestAdapter/TestingPlatformAdapter/MSTestBannerCapability.cs @@ -7,8 +7,6 @@ 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. - [SuppressMessage("ApiDesign", "RS0030:Do not use banned APIs", Justification = "We can use MTP from this folder")] internal sealed class MSTestBannerCapability : IBannerMessageOwnerCapability { diff --git a/src/Adapter/MSTest.TestAdapter/TestingPlatformAdapter/MSTestBridgedTestFramework.cs b/src/Adapter/MSTest.TestAdapter/TestingPlatformAdapter/MSTestBridgedTestFramework.cs index e529cae9cb..d09665cf53 100644 --- a/src/Adapter/MSTest.TestAdapter/TestingPlatformAdapter/MSTestBridgedTestFramework.cs +++ b/src/Adapter/MSTest.TestAdapter/TestingPlatformAdapter/MSTestBridgedTestFramework.cs @@ -53,7 +53,7 @@ protected override async Task SynchronizedRunTestsAsync(VSTestRunTestExecutionRe PlatformServiceProvider.Instance.AdapterTraceLogger = new BridgedTraceLogger(_loggerFactory.CreateLogger("mstest-trace")); MSTestExecutor testExecutor = new(cancellationToken); - await testExecutor.RunTestsAsync(request.AssemblyPaths, request.RunContext, request.FrameworkHandle, _configuration); + await testExecutor.RunTestsAsync(request.AssemblyPaths, request.RunContext, request.FrameworkHandle, _configuration).ConfigureAwait(false); } } #endif diff --git a/src/Adapter/MSTest.TestAdapter/TestingPlatformAdapter/MSTestGracefulStopTestExecutionCapability.cs b/src/Adapter/MSTest.TestAdapter/TestingPlatformAdapter/MSTestGracefulStopTestExecutionCapability.cs index ea80488cb6..01bce0f496 100644 --- a/src/Adapter/MSTest.TestAdapter/TestingPlatformAdapter/MSTestGracefulStopTestExecutionCapability.cs +++ b/src/Adapter/MSTest.TestAdapter/TestingPlatformAdapter/MSTestGracefulStopTestExecutionCapability.cs @@ -7,11 +7,8 @@ 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. - [SuppressMessage("ApiDesign", "RS0030:Do not use banned APIs", Justification = "We can use MTP from this folder")] internal sealed class MSTestGracefulStopTestExecutionCapability : IGracefulStopTestExecutionCapability -#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. { private MSTestGracefulStopTestExecutionCapability() { diff --git a/src/Adapter/MSTest.TestAdapter/TestingPlatformAdapter/TestApplicationBuilderExtensions.cs b/src/Adapter/MSTest.TestAdapter/TestingPlatformAdapter/TestApplicationBuilderExtensions.cs index 326c85f88f..4166a55daa 100644 --- a/src/Adapter/MSTest.TestAdapter/TestingPlatformAdapter/TestApplicationBuilderExtensions.cs +++ b/src/Adapter/MSTest.TestAdapter/TestingPlatformAdapter/TestApplicationBuilderExtensions.cs @@ -39,17 +39,13 @@ public static void AddMSTest(this ITestApplicationBuilder testApplicationBuilder testApplicationBuilder.AddRunSettingsService(extension); testApplicationBuilder.AddTestCaseFilterService(extension); testApplicationBuilder.AddTestRunParametersService(extension); -#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. testApplicationBuilder.AddMaximumFailedTestsService(extension); -#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. testApplicationBuilder.AddRunSettingsEnvironmentVariableProvider(extension); testApplicationBuilder.RegisterTestFramework( serviceProvider => new TestFrameworkCapabilities( new MSTestCapabilities(), -#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()), MSTestGracefulStopTestExecutionCapability.Instance), -#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/VSTestAdapter/MSTestDiscoverer.cs b/src/Adapter/MSTest.TestAdapter/VSTestAdapter/MSTestDiscoverer.cs index 7fcb8c825d..27fe223424 100644 --- a/src/Adapter/MSTest.TestAdapter/VSTestAdapter/MSTestDiscoverer.cs +++ b/src/Adapter/MSTest.TestAdapter/VSTestAdapter/MSTestDiscoverer.cs @@ -11,15 +11,15 @@ namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter; /// /// Contains the discovery logic for this adapter. /// -[DefaultExecutorUri(Constants.ExecutorUriString)] +[DefaultExecutorUri(MSTestAdapter.PlatformServices.EngineConstants.ExecutorUriString)] [FileExtension(".xap")] [FileExtension(".appx")] [FileExtension(".dll")] [FileExtension(".exe")] #if NET6_0_OR_GREATER -[Obsolete(Constants.PublicTypeObsoleteMessage, DiagnosticId = "MSTESTOBS")] +[Obsolete(TestTools.UnitTesting.FrameworkConstants.PublicTypeObsoleteMessage, DiagnosticId = "MSTESTOBS")] #else -[Obsolete(Constants.PublicTypeObsoleteMessage)] +[Obsolete(TestTools.UnitTesting.FrameworkConstants.PublicTypeObsoleteMessage)] #endif public class MSTestDiscoverer : ITestDiscoverer { diff --git a/src/Adapter/MSTest.TestAdapter/VSTestAdapter/MSTestExecutor.cs b/src/Adapter/MSTest.TestAdapter/VSTestAdapter/MSTestExecutor.cs index 35f125d683..7f041e0559 100644 --- a/src/Adapter/MSTest.TestAdapter/VSTestAdapter/MSTestExecutor.cs +++ b/src/Adapter/MSTest.TestAdapter/VSTestAdapter/MSTestExecutor.cs @@ -2,21 +2,24 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution; +using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices; using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface; +using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Resources; using Microsoft.VisualStudio.TestPlatform.ObjectModel; using Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter; using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging; +using Microsoft.VisualStudio.TestTools.UnitTesting; namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter; /// /// Contains the execution logic for this adapter. /// -[ExtensionUri(Constants.ExecutorUriString)] +[ExtensionUri(EngineConstants.ExecutorUriString)] #if NET6_0_OR_GREATER -[Obsolete(Constants.PublicTypeObsoleteMessage, DiagnosticId = "MSTESTOBS")] +[Obsolete(FrameworkConstants.PublicTypeObsoleteMessage, DiagnosticId = "MSTESTOBS")] #else -[Obsolete(Constants.PublicTypeObsoleteMessage)] +[Obsolete(FrameworkConstants.PublicTypeObsoleteMessage)] #endif public class MSTestExecutor : ITestExecutor { @@ -47,6 +50,20 @@ internal MSTestExecutor(CancellationToken cancellationToken) /// public TestExecutionManager TestExecutionManager { get; protected set; } +#pragma warning disable CA2255 // The 'ModuleInitializer' attribute should not be used in libraries + [ModuleInitializer] +#pragma warning restore CA2255 // The 'ModuleInitializer' attribute should not be used in libraries + internal static void EnsureAdapterAndFrameworkVersions() + { + string? adapterVersion = typeof(MSTestExecutor).Assembly.GetCustomAttribute()?.InformationalVersion; + string? frameworkVersion = typeof(TestMethodAttribute).Assembly.GetCustomAttribute()?.InformationalVersion; + if (adapterVersion is not null && frameworkVersion is not null + && adapterVersion != frameworkVersion) + { + throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, Resource.VersionMismatchBetweenAdapterAndFramework, adapterVersion, frameworkVersion)); + } + } + /// /// Runs the tests. /// @@ -82,7 +99,7 @@ internal async Task RunTestsAsync(IEnumerable? tests, IRunContext? run return; } - await RunTestsFromRightContextAsync(frameworkHandle, async testRunToken => await TestExecutionManager.RunTestsAsync(tests, runContext, frameworkHandle, testRunToken)); + await RunTestsFromRightContextAsync(frameworkHandle, async testRunToken => await TestExecutionManager.RunTestsAsync(tests, runContext, frameworkHandle, testRunToken).ConfigureAwait(false)).ConfigureAwait(false); } internal async Task RunTestsAsync(IEnumerable? sources, IRunContext? runContext, IFrameworkHandle? frameworkHandle, IConfiguration? configuration) @@ -96,7 +113,7 @@ internal async Task RunTestsAsync(IEnumerable? sources, IRunContext? run } sources = PlatformServiceProvider.Instance.TestSource.GetTestSources(sources); - await RunTestsFromRightContextAsync(frameworkHandle, async testRunToken => await TestExecutionManager.RunTestsAsync(sources, runContext, frameworkHandle, testRunToken)); + await RunTestsFromRightContextAsync(frameworkHandle, async testRunToken => await TestExecutionManager.RunTestsAsync(sources, runContext, frameworkHandle, testRunToken).ConfigureAwait(false)).ConfigureAwait(false); } /// @@ -126,7 +143,7 @@ private async Task RunTestsFromRightContextAsync(IFrameworkHandle frameworkHandl try { var threadTask = Task.Run(entryPointThread.Join, _cancellationToken); - await threadTask; + await threadTask.ConfigureAwait(false); } catch (Exception ex) { @@ -142,7 +159,7 @@ private async Task RunTestsFromRightContextAsync(IFrameworkHandle frameworkHandl frameworkHandle.SendMessage(TestMessageLevel.Warning, Resource.STAIsOnlySupportedOnWindowsWarning); } - await DoRunTestsAsync(); + await DoRunTestsAsync().ConfigureAwait(false); } // Local functions @@ -152,8 +169,8 @@ async Task DoRunTestsAsync() { try { - _testRunCancellationToken = new TestRunCancellationToken(); - await runTestsAction(_testRunCancellationToken); + _testRunCancellationToken = new TestRunCancellationToken(_cancellationToken); + await runTestsAction(_testRunCancellationToken).ConfigureAwait(false); } finally { diff --git a/src/Adapter/MSTest.TestAdapter/buildTransitive/common/MSTest.TestAdapter.props b/src/Adapter/MSTest.TestAdapter/buildTransitive/common/MSTest.TestAdapter.props index 639ce61536..1e14ef753e 100644 --- a/src/Adapter/MSTest.TestAdapter/buildTransitive/common/MSTest.TestAdapter.props +++ b/src/Adapter/MSTest.TestAdapter/buildTransitive/common/MSTest.TestAdapter.props @@ -2,6 +2,7 @@ true + true diff --git a/src/Adapter/MSTest.TestAdapter/buildTransitive/common/MSTest.TestAdapter.targets b/src/Adapter/MSTest.TestAdapter/buildTransitive/common/MSTest.TestAdapter.targets index 63ad3b5b08..06b5afe773 100644 --- a/src/Adapter/MSTest.TestAdapter/buildTransitive/common/MSTest.TestAdapter.targets +++ b/src/Adapter/MSTest.TestAdapter/buildTransitive/common/MSTest.TestAdapter.targets @@ -1,4 +1,10 @@ - + + + + + @@ -45,6 +51,10 @@ + + + + diff --git a/src/Adapter/MSTest.TestAdapter/buildTransitive/uwp/MSTest.TestAdapter.props b/src/Adapter/MSTest.TestAdapter/buildTransitive/uwp/MSTest.TestAdapter.props index 259e47a969..d38f1c2bdf 100644 --- a/src/Adapter/MSTest.TestAdapter/buildTransitive/uwp/MSTest.TestAdapter.props +++ b/src/Adapter/MSTest.TestAdapter/buildTransitive/uwp/MSTest.TestAdapter.props @@ -2,6 +2,7 @@ true + true diff --git a/src/Adapter/MSTest.TestAdapter/buildTransitive/uwp/MSTest.TestAdapter.targets b/src/Adapter/MSTest.TestAdapter/buildTransitive/uwp/MSTest.TestAdapter.targets index e8d37601f3..46bf3f788d 100644 --- a/src/Adapter/MSTest.TestAdapter/buildTransitive/uwp/MSTest.TestAdapter.targets +++ b/src/Adapter/MSTest.TestAdapter/buildTransitive/uwp/MSTest.TestAdapter.targets @@ -27,6 +27,10 @@ + + + + @@ -40,15 +40,16 @@ + - - - true - - + + + + + @@ -60,22 +61,9 @@ - - - True - True - Resource.resx - - - ResXFileCodeGenerator - Resource.Designer.cs - Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices - Designer - - - + diff --git a/src/Adapter/MSTest.TestAdapter/MSTestSettings.cs b/src/Adapter/MSTestAdapter.PlatformServices/MSTestSettings.cs similarity index 97% rename from src/Adapter/MSTest.TestAdapter/MSTestSettings.cs rename to src/Adapter/MSTestAdapter.PlatformServices/MSTestSettings.cs index 65381bcc72..1637d7f3f0 100644 --- a/src/Adapter/MSTest.TestAdapter/MSTestSettings.cs +++ b/src/Adapter/MSTestAdapter.PlatformServices/MSTestSettings.cs @@ -2,15 +2,17 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel; -using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface; #if !WINDOWS_UWP using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices; #endif +using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface; using Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter; using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging; using Microsoft.VisualStudio.TestPlatform.ObjectModel.Utilities; using Microsoft.VisualStudio.TestTools.UnitTesting; +using ExecutionScope = Microsoft.VisualStudio.TestTools.UnitTesting.ExecutionScope; + namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter; /// @@ -18,9 +20,9 @@ namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter; /// [Serializable] #if NET6_0_OR_GREATER -[Obsolete(Constants.PublicTypeObsoleteMessage, DiagnosticId = "MSTESTOBS")] +[Obsolete(FrameworkConstants.PublicTypeObsoleteMessage, DiagnosticId = "MSTESTOBS")] #else -[Obsolete(Constants.PublicTypeObsoleteMessage)] +[Obsolete(FrameworkConstants.PublicTypeObsoleteMessage)] #endif public class MSTestSettings { @@ -260,9 +262,6 @@ public static void PopulateSettings(MSTestSettings settings) #if !WINDOWS_UWP private static bool IsRunSettingsFileHasMSTestSettings(string? runSettingsXml) - => IsRunSettingsFileHasSettingName(runSettingsXml, SettingsName) || IsRunSettingsFileHasSettingName(runSettingsXml, SettingsNameAlias); - - private static bool IsRunSettingsFileHasSettingName(string? runSettingsXml, string SettingName) { if (StringEx.IsNullOrWhiteSpace(runSettingsXml)) { @@ -277,7 +276,8 @@ private static bool IsRunSettingsFileHasSettingName(string? runSettingsXml, stri reader.ReadToNextElement(); // Read till we reach nodeName element or reach EOF - while (!string.Equals(reader.Name, SettingName, StringComparison.OrdinalIgnoreCase) + while (!string.Equals(reader.Name, SettingsName, StringComparison.OrdinalIgnoreCase) + && !string.Equals(reader.Name, SettingsNameAlias, StringComparison.OrdinalIgnoreCase) && !reader.EOF) { reader.SkipToNextElement(); @@ -308,6 +308,8 @@ internal static void PopulateSettings(IDiscoveryContext? context, IMessageLogger var settings = new MSTestSettings(); var runConfigurationSettings = RunConfigurationSettings.PopulateSettings(context); + // We have runsettings, but we don't have testconfig. + // Just use runsettings. #if !WINDOWS_UWP if (!StringEx.IsNullOrEmpty(context?.RunSettings?.SettingsXml) && configuration?["mstest"] is null) @@ -495,7 +497,7 @@ private static MSTestSettings ToSettings(XmlReader reader, IMessageLogger? logge CultureInfo.CurrentCulture, Resource.InvalidClassCleanupLifecycleValue, value, - string.Join(", ", EnumPolyfill.GetNames()))); + string.Join(", ", Enum.GetNames()))); break; } @@ -822,7 +824,7 @@ private static void SetParallelSettings(XmlReader reader, MSTestSettings setting CultureInfo.CurrentCulture, Resource.InvalidParallelScopeValue, value, - string.Join(", ", EnumPolyfill.GetNames()))); + string.Join(", ", Enum.GetNames()))); break; } @@ -833,8 +835,8 @@ private static void SetParallelSettings(XmlReader reader, MSTestSettings setting string.Format( CultureInfo.CurrentCulture, Resource.InvalidSettingsXmlElement, - ParallelizeSettingsName, - reader.Name)); + reader.Name, + ParallelizeSettingsName)); } } } @@ -849,7 +851,7 @@ private static void SetParallelSettings(XmlReader reader, MSTestSettings setting private static bool TryParseEnum(string value, out T result) where T : struct, Enum => Enum.TryParse(value, true, out result) - && EnumPolyfill.IsDefined(result); + && Enum.IsDefined(result); private static void SetGlobalSettings( [StringSyntax(StringSyntaxAttribute.Xml, nameof(runsettingsXml))] string runsettingsXml, @@ -978,7 +980,7 @@ internal static void SetSettingsFromConfig(IConfiguration configuration, IMessag CultureInfo.CurrentCulture, Resource.InvalidClassCleanupLifecycleValue, classCleanupLifecycle, - string.Join(", ", EnumPolyfill.GetNames()))); + string.Join(", ", Enum.GetNames()))); } settings.ClassCleanupLifecycle = lifecycle; @@ -992,9 +994,9 @@ internal static void SetSettingsFromConfig(IConfiguration configuration, IMessag : parallelWorkers > 0 ? parallelWorkers : throw new AdapterSettingsException(string.Format( - CultureInfo.CurrentCulture, - Resource.InvalidParallelWorkersValue, - workers)) + CultureInfo.CurrentCulture, + Resource.InvalidParallelWorkersValue, + workers)) : throw new AdapterSettingsException(string.Format( CultureInfo.CurrentCulture, Resource.InvalidParallelWorkersValue, @@ -1011,7 +1013,7 @@ internal static void SetSettingsFromConfig(IConfiguration configuration, IMessag CultureInfo.CurrentCulture, Resource.InvalidParallelScopeValue, value, - string.Join(", ", EnumPolyfill.GetNames()))); + string.Join(", ", Enum.GetNames()))); } settings.ParallelizationScope = scope; diff --git a/src/Adapter/MSTest.TestAdapter/ObjectModel/AdapterSettingsException.cs b/src/Adapter/MSTestAdapter.PlatformServices/ObjectModel/AdapterSettingsException.cs similarity index 100% rename from src/Adapter/MSTest.TestAdapter/ObjectModel/AdapterSettingsException.cs rename to src/Adapter/MSTestAdapter.PlatformServices/ObjectModel/AdapterSettingsException.cs diff --git a/src/Adapter/MSTest.TestAdapter/ObjectModel/DynamicDataType.cs b/src/Adapter/MSTestAdapter.PlatformServices/ObjectModel/DynamicDataType.cs similarity index 75% rename from src/Adapter/MSTest.TestAdapter/ObjectModel/DynamicDataType.cs rename to src/Adapter/MSTestAdapter.PlatformServices/ObjectModel/DynamicDataType.cs index 572a628955..6046f3dfd8 100644 --- a/src/Adapter/MSTest.TestAdapter/ObjectModel/DynamicDataType.cs +++ b/src/Adapter/MSTestAdapter.PlatformServices/ObjectModel/DynamicDataType.cs @@ -13,13 +13,8 @@ internal enum DynamicDataType : int /// None = 0, - /// - /// Dynamic data from . - /// - DataSourceAttribute = 1, - /// /// Dynamic data from . /// - ITestDataSource = 2, + ITestDataSource = 1, } diff --git a/src/Adapter/MSTest.TestAdapter/ObjectModel/FixtureTestResult.cs b/src/Adapter/MSTestAdapter.PlatformServices/ObjectModel/FixtureTestResult.cs similarity index 100% rename from src/Adapter/MSTest.TestAdapter/ObjectModel/FixtureTestResult.cs rename to src/Adapter/MSTestAdapter.PlatformServices/ObjectModel/FixtureTestResult.cs diff --git a/src/Adapter/MSTest.TestAdapter/ObjectModel/StackTraceInformation.cs b/src/Adapter/MSTestAdapter.PlatformServices/ObjectModel/StackTraceInformation.cs similarity index 100% rename from src/Adapter/MSTest.TestAdapter/ObjectModel/StackTraceInformation.cs rename to src/Adapter/MSTestAdapter.PlatformServices/ObjectModel/StackTraceInformation.cs diff --git a/src/Adapter/MSTest.TestAdapter/ObjectModel/TestAssemblySettings.cs b/src/Adapter/MSTestAdapter.PlatformServices/ObjectModel/TestAssemblySettings.cs similarity index 92% rename from src/Adapter/MSTest.TestAdapter/ObjectModel/TestAssemblySettings.cs rename to src/Adapter/MSTestAdapter.PlatformServices/ObjectModel/TestAssemblySettings.cs index 5e9a68270d..8854d53de3 100644 --- a/src/Adapter/MSTest.TestAdapter/ObjectModel/TestAssemblySettings.cs +++ b/src/Adapter/MSTestAdapter.PlatformServices/ObjectModel/TestAssemblySettings.cs @@ -3,6 +3,8 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; +using ExecutionScope = Microsoft.VisualStudio.TestTools.UnitTesting.ExecutionScope; + namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel; [Serializable] diff --git a/src/Adapter/MSTest.TestAdapter/ObjectModel/TestFailedException.cs b/src/Adapter/MSTestAdapter.PlatformServices/ObjectModel/TestFailedException.cs similarity index 100% rename from src/Adapter/MSTest.TestAdapter/ObjectModel/TestFailedException.cs rename to src/Adapter/MSTestAdapter.PlatformServices/ObjectModel/TestFailedException.cs diff --git a/src/Adapter/MSTest.TestAdapter/ObjectModel/TestMethod.cs b/src/Adapter/MSTestAdapter.PlatformServices/ObjectModel/TestMethod.cs similarity index 90% rename from src/Adapter/MSTest.TestAdapter/ObjectModel/TestMethod.cs rename to src/Adapter/MSTestAdapter.PlatformServices/ObjectModel/TestMethod.cs index 2bb1e1a1b6..a9c0f1e450 100644 --- a/src/Adapter/MSTest.TestAdapter/ObjectModel/TestMethod.cs +++ b/src/Adapter/MSTestAdapter.PlatformServices/ObjectModel/TestMethod.cs @@ -16,9 +16,9 @@ namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel; /// TestMethod contains information about a unit test method that needs to be executed. /// #if NET6_0_OR_GREATER -[Obsolete(Constants.PublicTypeObsoleteMessage, DiagnosticId = "MSTESTOBS")] +[Obsolete(FrameworkConstants.PublicTypeObsoleteMessage, DiagnosticId = "MSTESTOBS")] #else -[Obsolete(Constants.PublicTypeObsoleteMessage)] +[Obsolete(FrameworkConstants.PublicTypeObsoleteMessage)] #endif [Serializable] public sealed class TestMethod : ITestMethod @@ -89,7 +89,7 @@ internal TestMethod(string? managedTypeName, string? managedMethodName, string?[ /// public string? DeclaringAssemblyName { - get => field; + get; set { @@ -106,7 +106,7 @@ public string? DeclaringAssemblyName /// public string? DeclaringClassFullName { - get => field; + get; set { @@ -147,6 +147,15 @@ public string? DeclaringClassFullName /// internal string?[]? SerializedData { get; set; } + // This holds user types that may not be serializable. + // If app domains are enabled, we have no choice other than losing the original data. + // In that case, we fallback to deserializing the SerializedData. + [field: NonSerialized] + internal object?[]? ActualData { get; set; } + + [field: NonSerialized] + internal MethodInfo? MethodInfo { get; set; } + /// /// Gets or sets the test data source ignore message. /// @@ -161,14 +170,9 @@ public string? DeclaringClassFullName internal string? TestGroup { get; set; } /// - /// Gets the display name set during discovery. + /// Gets or sets the display name set during discovery. /// - internal string DisplayName { get; } - - internal string UniqueName - => HasManagedMethodAndTypeProperties - ? $"{ManagedTypeName}.{ManagedMethodName}->{string.Join(", ", SerializedData ?? [])}" - : $"{FullClassName}.{Name}->{string.Join(", ", SerializedData ?? [])}"; + internal string DisplayName { get; set; } internal TestMethod Clone() => (TestMethod)MemberwiseClone(); } diff --git a/src/Adapter/MSTest.TestAdapter/ObjectModel/TypeInspectionException.cs b/src/Adapter/MSTestAdapter.PlatformServices/ObjectModel/TypeInspectionException.cs similarity index 100% rename from src/Adapter/MSTest.TestAdapter/ObjectModel/TypeInspectionException.cs rename to src/Adapter/MSTestAdapter.PlatformServices/ObjectModel/TypeInspectionException.cs diff --git a/src/Adapter/MSTest.TestAdapter/ObjectModel/UnitTestElement.cs b/src/Adapter/MSTestAdapter.PlatformServices/ObjectModel/UnitTestElement.cs similarity index 82% rename from src/Adapter/MSTest.TestAdapter/ObjectModel/UnitTestElement.cs rename to src/Adapter/MSTestAdapter.PlatformServices/ObjectModel/UnitTestElement.cs index 54bcc8fb98..a84baae914 100644 --- a/src/Adapter/MSTest.TestAdapter/ObjectModel/UnitTestElement.cs +++ b/src/Adapter/MSTestAdapter.PlatformServices/ObjectModel/UnitTestElement.cs @@ -3,6 +3,7 @@ using Microsoft.TestPlatform.AdapterUtilities; using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Extensions; +using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices; using Microsoft.VisualStudio.TestPlatform.ObjectModel; using Microsoft.VisualStudio.TestTools.UnitTesting; @@ -61,6 +62,7 @@ public UnitTestElement(TestMethod testMethod) /// /// Gets or sets the DisplayName. /// + // TODO: Remove this property and simply use TestMethod.DisplayName public string? DisplayName { get; set; } /// @@ -103,48 +105,49 @@ internal TestCase ToTestCase() { // This causes compatibility problems with older runners. // string testFullName = this.TestMethod.HasManagedMethodAndTypeProperties - // ? string.Format(CultureInfo.InvariantCulture, "{0}.{1}", this.TestMethod.ManagedTypeName, this.TestMethod.ManagedMethodName) - // : string.Format(CultureInfo.InvariantCulture, "{0}.{1}", this.TestMethod.FullClassName, this.TestMethod.Name); - string testFullName = string.Format(CultureInfo.InvariantCulture, "{0}.{1}", TestMethod.FullClassName, TestMethod.Name); + // ? $"{TestMethod.ManagedTypeName}.{TestMethod.ManagedMethodName}" + // : $"{TestMethod.FullClassName}.{TestMethod.Name}"; + string testFullName = $"{TestMethod.FullClassName}.{TestMethod.Name}"; - TestCase testCase = new(testFullName, Constants.ExecutorUri, TestMethod.AssemblyName) + TestCase testCase = new(testFullName, EngineConstants.ExecutorUri, TestMethod.AssemblyName) { DisplayName = GetDisplayName(), + LocalExtensionData = this, }; if (TestMethod.HasManagedMethodAndTypeProperties) { testCase.SetPropertyValue(TestCaseExtensions.ManagedTypeProperty, TestMethod.ManagedTypeName); testCase.SetPropertyValue(TestCaseExtensions.ManagedMethodProperty, TestMethod.ManagedMethodName); - testCase.SetPropertyValue(Constants.TestClassNameProperty, TestMethod.ManagedTypeName); + testCase.SetPropertyValue(EngineConstants.TestClassNameProperty, TestMethod.ManagedTypeName); } else { - testCase.SetPropertyValue(Constants.TestClassNameProperty, TestMethod.FullClassName); + testCase.SetPropertyValue(EngineConstants.TestClassNameProperty, TestMethod.FullClassName); } IReadOnlyCollection hierarchy = TestMethod.Hierarchy; if (hierarchy is { Count: > 0 }) { - testCase.SetHierarchy(hierarchy.ToArray()); + testCase.SetHierarchy([.. hierarchy]); } // Set declaring type if present so the correct method info can be retrieved if (TestMethod.DeclaringClassFullName != null) { - testCase.SetPropertyValue(Constants.DeclaringClassNameProperty, TestMethod.DeclaringClassFullName); + testCase.SetPropertyValue(EngineConstants.DeclaringClassNameProperty, TestMethod.DeclaringClassFullName); } // Set only if some test category is present if (TestCategory is { Length: > 0 }) { - testCase.SetPropertyValue(Constants.TestCategoryProperty, TestCategory); + testCase.SetPropertyValue(EngineConstants.TestCategoryProperty, TestCategory); } // Set priority if present if (Priority != null) { - testCase.SetPropertyValue(Constants.PriorityProperty, Priority.Value); + testCase.SetPropertyValue(EngineConstants.PriorityProperty, Priority.Value); } if (Traits is { Length: > 0 }) @@ -154,34 +157,29 @@ internal TestCase ToTestCase() if (!StringEx.IsNullOrEmpty(CssIteration)) { - testCase.SetPropertyValue(Constants.CssIterationProperty, CssIteration); + testCase.SetPropertyValue(EngineConstants.CssIterationProperty, CssIteration); } if (!StringEx.IsNullOrEmpty(CssProjectStructure)) { - testCase.SetPropertyValue(Constants.CssProjectStructureProperty, CssProjectStructure); - } - - if (!StringEx.IsNullOrEmpty(Description)) - { - testCase.SetPropertyValue(Constants.DescriptionProperty, Description); + testCase.SetPropertyValue(EngineConstants.CssProjectStructureProperty, CssProjectStructure); } if (WorkItemIds != null) { - testCase.SetPropertyValue(Constants.WorkItemIdsProperty, WorkItemIds); + testCase.SetPropertyValue(EngineConstants.WorkItemIdsProperty, WorkItemIds); } // The list of items to deploy before running this test. if (DeploymentItems is { Length: > 0 }) { - testCase.SetPropertyValue(Constants.DeploymentItemsProperty, DeploymentItems); + testCase.SetPropertyValue(EngineConstants.DeploymentItemsProperty, DeploymentItems); } // Set the Do not parallelize state if present if (DoNotParallelize) { - testCase.SetPropertyValue(Constants.DoNotParallelizeProperty, DoNotParallelize); + testCase.SetPropertyValue(EngineConstants.DoNotParallelizeProperty, DoNotParallelize); } // Store resolved data if any @@ -189,12 +187,12 @@ internal TestCase ToTestCase() { string?[]? data = TestMethod.SerializedData; - testCase.SetPropertyValue(Constants.TestDynamicDataTypeProperty, (int)TestMethod.DataType); - testCase.SetPropertyValue(Constants.TestDynamicDataProperty, data); + testCase.SetPropertyValue(EngineConstants.TestDynamicDataTypeProperty, (int)TestMethod.DataType); + testCase.SetPropertyValue(EngineConstants.TestDynamicDataProperty, data); // VSTest serialization doesn't handle null so instead don't set the property so that it's deserialized as null if (TestMethod.TestDataSourceIgnoreMessage is not null) { - testCase.SetPropertyValue(Constants.TestDataSourceIgnoreMessageProperty, TestMethod.TestDataSourceIgnoreMessage); + testCase.SetPropertyValue(EngineConstants.TestDataSourceIgnoreMessageProperty, TestMethod.TestDataSourceIgnoreMessage); } } @@ -205,7 +203,7 @@ internal TestCase ToTestCase() private void SetTestCaseId(TestCase testCase, string testFullName) { - testCase.SetPropertyValue(Constants.TestIdGenerationStrategyProperty, (int)TestMethod.TestIdGenerationStrategy); + testCase.SetPropertyValue(EngineConstants.TestIdGenerationStrategyProperty, (int)TestMethod.TestIdGenerationStrategy); switch (TestMethod.TestIdGenerationStrategy) { @@ -277,7 +275,7 @@ private Guid GenerateSerializedDataStrategyTestId(string testFullName) { var idProvider = new TestIdProvider(); - idProvider.AppendString(Constants.ExecutorUriString); + idProvider.AppendString(EngineConstants.ExecutorUriString); // Below comment is copied over from Test Platform. // If source is a file name then just use the filename for the identifier since the file might have moved between diff --git a/src/Adapter/MSTest.TestAdapter/ObjectModel/UnitTestOutcome.cs b/src/Adapter/MSTestAdapter.PlatformServices/ObjectModel/UnitTestOutcome.cs similarity index 89% rename from src/Adapter/MSTest.TestAdapter/ObjectModel/UnitTestOutcome.cs rename to src/Adapter/MSTestAdapter.PlatformServices/ObjectModel/UnitTestOutcome.cs index a519c454eb..4b8eaddc68 100644 --- a/src/Adapter/MSTest.TestAdapter/ObjectModel/UnitTestOutcome.cs +++ b/src/Adapter/MSTestAdapter.PlatformServices/ObjectModel/UnitTestOutcome.cs @@ -7,9 +7,9 @@ namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel; /// Outcome of a test. /// #if NET6_0_OR_GREATER -[Obsolete(Constants.PublicTypeObsoleteMessage, DiagnosticId = "MSTESTOBS")] +[Obsolete(TestTools.UnitTesting.FrameworkConstants.PublicTypeObsoleteMessage, DiagnosticId = "MSTESTOBS")] #else -[Obsolete(Constants.PublicTypeObsoleteMessage)] +[Obsolete(TestTools.UnitTesting.FrameworkConstants.PublicTypeObsoleteMessage)] #endif public enum UnitTestOutcome : int { diff --git a/src/Adapter/MSTest.TestAdapter/ObjectModel/UnitTestResult.cs b/src/Adapter/MSTestAdapter.PlatformServices/ObjectModel/UnitTestResult.cs similarity index 96% rename from src/Adapter/MSTest.TestAdapter/ObjectModel/UnitTestResult.cs rename to src/Adapter/MSTestAdapter.PlatformServices/ObjectModel/UnitTestResult.cs index c317fa9dde..251e6df0e9 100644 --- a/src/Adapter/MSTest.TestAdapter/ObjectModel/UnitTestResult.cs +++ b/src/Adapter/MSTestAdapter.PlatformServices/ObjectModel/UnitTestResult.cs @@ -11,9 +11,9 @@ namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel; [Serializable] [DebuggerDisplay("{DisplayName} ({Outcome})")] #if NET6_0_OR_GREATER -[Obsolete(Constants.PublicTypeObsoleteMessage, DiagnosticId = "MSTESTOBS")] +[Obsolete(TestTools.UnitTesting.FrameworkConstants.PublicTypeObsoleteMessage, DiagnosticId = "MSTESTOBS")] #else -[Obsolete(Constants.PublicTypeObsoleteMessage)] +[Obsolete(TestTools.UnitTesting.FrameworkConstants.PublicTypeObsoleteMessage)] #endif public class UnitTestResult { diff --git a/src/Adapter/MSTest.TestAdapter/PlatformServiceProvider.cs b/src/Adapter/MSTestAdapter.PlatformServices/PlatformServiceProvider.cs similarity index 92% rename from src/Adapter/MSTest.TestAdapter/PlatformServiceProvider.cs rename to src/Adapter/MSTestAdapter.PlatformServices/PlatformServiceProvider.cs index 21b7acea5b..755127a1eb 100644 --- a/src/Adapter/MSTest.TestAdapter/PlatformServiceProvider.cs +++ b/src/Adapter/MSTestAdapter.PlatformServices/PlatformServiceProvider.cs @@ -15,8 +15,6 @@ namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter; /// internal sealed class PlatformServiceProvider : IPlatformServiceProvider { - private static readonly Action CancelDelegate = static state => ((TestContextImplementation)state!).Context.CancellationTokenSource.Cancel(); - /// /// Initializes a new instance of the class - a singleton. /// @@ -194,9 +192,6 @@ public ITestSourceHost CreateTestSourceHost( /// /// The test method. /// - /// - /// The writer instance for logging. - /// /// /// The default set of properties the test context needs to be filled with. /// @@ -208,10 +203,9 @@ public ITestSourceHost CreateTestSourceHost( /// /// This was required for compatibility reasons since the TestContext object that the V1 adapter had for desktop is not .Net Core compliant. /// - public ITestContext GetTestContext(ITestMethod testMethod, StringWriter writer, IDictionary properties, IMessageLogger messageLogger, UTF.UnitTestOutcome outcome) + public ITestContext GetTestContext(ITestMethod testMethod, IDictionary properties, IMessageLogger messageLogger, UTF.UnitTestOutcome outcome) { - var testContextImplementation = new TestContextImplementation(testMethod, writer, properties, messageLogger); - TestRunCancellationToken?.Register(CancelDelegate, testContextImplementation); + var testContextImplementation = new TestContextImplementation(testMethod, properties, messageLogger, TestRunCancellationToken); testContextImplementation.SetOutcome(outcome); return testContextImplementation; } diff --git a/src/Adapter/MSTestAdapter.PlatformServices/PublicAPI/PublicAPI.Shipped.txt b/src/Adapter/MSTestAdapter.PlatformServices/PublicAPI/PublicAPI.Shipped.txt index 6a36504523..d60473368b 100644 --- a/src/Adapter/MSTestAdapter.PlatformServices/PublicAPI/PublicAPI.Shipped.txt +++ b/src/Adapter/MSTestAdapter.PlatformServices/PublicAPI/PublicAPI.Shipped.txt @@ -1,4 +1,132 @@ #nullable enable +const Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestMethodInfo.TimeoutWhenNotSet = 0 -> int +const Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.MSTestSettings.SettingsName = "MSTest" -> string! +const Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.MSTestSettings.SettingsNameAlias = "MSTestV2" -> string! +const Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.TestMethod.TotalHierarchyLevels = 4 -> int +const Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.RunConfigurationSettings.SettingsName = "RunConfiguration" -> string! +Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.LogMessageListener +Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.LogMessageListener.DebugTrace.get -> string? +Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.LogMessageListener.Dispose() -> void +Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.LogMessageListener.GetAndClearDebugTrace() -> string? +Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.LogMessageListener.GetAndClearStandardError() -> string? +Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.LogMessageListener.GetAndClearStandardOutput() -> string? +Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.LogMessageListener.LogMessageListener(bool captureDebugTraces) -> void +Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.LogMessageListener.StandardError.get -> string! +Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.LogMessageListener.StandardOutput.get -> string! +Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestAssemblyInfo +Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestAssemblyInfo.AssemblyCleanupMethod.get -> System.Reflection.MethodInfo? +Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestAssemblyInfo.AssemblyInitializationException.get -> System.Exception? +Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestAssemblyInfo.AssemblyInitializeMethod.get -> System.Reflection.MethodInfo? +Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestAssemblyInfo.HasExecutableCleanupMethod.get -> bool +Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestAssemblyInfo.IsAssemblyInitializeExecuted.get -> bool +Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestAssemblyInfo.RunAssemblyCleanup() -> string? +Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestAssemblyInfo.RunAssemblyInitialize(Microsoft.VisualStudio.TestTools.UnitTesting.TestContext! testContext) -> void +Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestClassInfo +Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestClassInfo.BaseClassCleanupMethodsStack.get -> System.Collections.Generic.Stack! +Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestClassInfo.BaseClassInitAndCleanupMethods.get -> System.Collections.Generic.Queue!>! +Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestClassInfo.BaseTestCleanupMethodsQueue.get -> System.Collections.Generic.Queue! +Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestClassInfo.BaseTestInitializeMethodsQueue.get -> System.Collections.Generic.Queue! +Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestClassInfo.ClassAttribute.get -> Microsoft.VisualStudio.TestTools.UnitTesting.TestClassAttribute! +Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestClassInfo.ClassCleanupException.get -> System.Exception? +Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestClassInfo.ClassCleanupMethod.get -> System.Reflection.MethodInfo? +Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestClassInfo.ClassInitializationException.get -> System.Exception? +Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestClassInfo.ClassInitializeMethod.get -> System.Reflection.MethodInfo? +Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestClassInfo.ClassType.get -> System.Type! +Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestClassInfo.Constructor.get -> System.Reflection.ConstructorInfo! +Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestClassInfo.HasExecutableCleanupMethod.get -> bool +Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestClassInfo.IsClassCleanupExecuted.get -> bool +Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestClassInfo.IsClassInitializeExecuted.get -> bool +Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestClassInfo.Parent.get -> Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestAssemblyInfo! +Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestClassInfo.RunClassCleanup(Microsoft.VisualStudio.TestTools.UnitTesting.ClassCleanupBehavior classCleanupLifecycle = Microsoft.VisualStudio.TestTools.UnitTesting.ClassCleanupBehavior.EndOfAssembly) -> string? +Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestClassInfo.RunClassInitialize(Microsoft.VisualStudio.TestTools.UnitTesting.TestContext! testContext) -> void +Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestClassInfo.TestCleanupMethod.get -> System.Reflection.MethodInfo? +Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestClassInfo.TestContextProperty.get -> System.Reflection.PropertyInfo? +Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestClassInfo.TestInitializeMethod.get -> System.Reflection.MethodInfo? +Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestExecutionManager +Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestExecutionManager.RunTests(System.Collections.Generic.IEnumerable! tests, Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter.IRunContext? runContext, Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter.IFrameworkHandle! frameworkHandle, Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.TestRunCancellationToken! runCancellationToken) -> void +Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestExecutionManager.RunTests(System.Collections.Generic.IEnumerable! sources, Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter.IRunContext? runContext, Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter.IFrameworkHandle! frameworkHandle, Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.TestRunCancellationToken! cancellationToken) -> void +Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestExecutionManager.TestExecutionManager() -> void +Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestMethodInfo +Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestMethodInfo.Arguments.get -> object?[]? +Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestMethodInfo.GetAllAttributes(bool inherit) -> System.Attribute![]? +Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestMethodInfo.GetAttributes(bool inherit) -> TAttributeType![]! +Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestMethodInfo.IsRunnable.get -> bool +Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestMethodInfo.IsTimeoutSet.get -> bool +Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestMethodInfo.MethodInfo.get -> System.Reflection.MethodInfo! +Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestMethodInfo.NotRunnableReason.get -> string? +Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestMethodInfo.ParameterTypes.get -> System.Reflection.ParameterInfo![]! +Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestMethodInfo.ReturnType.get -> System.Type! +Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestMethodInfo.TestClassName.get -> string! +Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestMethodInfo.TestMethodName.get -> string! +Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Extensions.TestResultExtensions +Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Extensions.UnitTestOutcomeExtensions +Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.MSTestSettings +Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.MSTestSettings.CaptureDebugTraces.get -> bool +Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.MSTestSettings.ClassCleanupLifecycle.get -> Microsoft.VisualStudio.TestTools.UnitTesting.ClassCleanupBehavior? +Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.MSTestSettings.DisableParallelization.get -> bool +Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.MSTestSettings.EnableBaseClassTestMethodsFromOtherAssemblies.get -> bool +Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.MSTestSettings.ForcedLegacyMode.get -> bool +Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.MSTestSettings.MapInconclusiveToFailed.get -> bool +Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.MSTestSettings.MapNotRunnableToFailed.get -> bool +Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.MSTestSettings.MSTestSettings() -> void +Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.MSTestSettings.ParallelizationScope.get -> Microsoft.VisualStudio.TestTools.UnitTesting.ExecutionScope? +Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.MSTestSettings.ParallelizationWorkers.get -> int? +Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.MSTestSettings.TestSettingsFile.get -> string? +Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.MSTestSettings.TestTimeout.get -> int +Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.MSTestSettings.TreatClassAndAssemblyCleanupWarningsAsErrors.get -> bool +Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.MSTestSettings.TreatDiscoveryWarningsAsErrors.get -> bool +Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.TestMethod +Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.TestMethod.AssemblyName.get -> string! +Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.TestMethod.DeclaringAssemblyName.get -> string? +Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.TestMethod.DeclaringAssemblyName.set -> void +Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.TestMethod.DeclaringClassFullName.get -> string? +Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.TestMethod.DeclaringClassFullName.set -> void +Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.TestMethod.FullClassName.get -> string! +Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.TestMethod.HasManagedMethodAndTypeProperties.get -> bool +Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.TestMethod.Hierarchy.get -> System.Collections.Generic.IReadOnlyCollection! +Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.TestMethod.IsAsync.get -> bool +Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.TestMethod.ManagedMethodName.get -> string? +Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.TestMethod.ManagedTypeName.get -> string? +Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.TestMethod.Name.get -> string! +Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.TestMethod.TestIdGenerationStrategy.get -> Microsoft.VisualStudio.TestTools.UnitTesting.TestIdGenerationStrategy +Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.TestMethod.TestMethod(string! name, string! fullClassName, string! assemblyName, bool isAsync) -> void +Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.UnitTestOutcome +Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.UnitTestOutcome.Error = 0 -> Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.UnitTestOutcome +Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.UnitTestOutcome.Failed = 1 -> Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.UnitTestOutcome +Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.UnitTestOutcome.Ignored = 4 -> Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.UnitTestOutcome +Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.UnitTestOutcome.Inconclusive = 3 -> Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.UnitTestOutcome +Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.UnitTestOutcome.InProgress = 8 -> Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.UnitTestOutcome +Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.UnitTestOutcome.NotFound = 7 -> Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.UnitTestOutcome +Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.UnitTestOutcome.NotRunnable = 5 -> Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.UnitTestOutcome +Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.UnitTestOutcome.Passed = 6 -> Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.UnitTestOutcome +Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.UnitTestOutcome.Timeout = 2 -> Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.UnitTestOutcome +Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.UnitTestResult +Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.UnitTestResult.DatarowIndex.get -> int +Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.UnitTestResult.DebugTrace.get -> string? +Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.UnitTestResult.DisplayName.get -> string? +Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.UnitTestResult.Duration.get -> System.TimeSpan +Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.UnitTestResult.ErrorColumnNumber.get -> int +Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.UnitTestResult.ErrorFilePath.get -> string? +Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.UnitTestResult.ErrorLineNumber.get -> int +Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.UnitTestResult.ErrorMessage.get -> string? +Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.UnitTestResult.ErrorStackTrace.get -> string? +Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.UnitTestResult.ExecutionId.get -> System.Guid +Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.UnitTestResult.InnerResultsCount.get -> int +Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.UnitTestResult.Outcome.get -> Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.UnitTestOutcome +Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.UnitTestResult.ParentExecId.get -> System.Guid +Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.UnitTestResult.ResultFiles.get -> System.Collections.Generic.IList? +Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.UnitTestResult.StandardError.get -> string? +Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.UnitTestResult.StandardOut.get -> string? +Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.UnitTestResult.TestContextMessages.get -> string? +Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.RunConfigurationSettings +Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.RunConfigurationSettings.CollectSourceInformation.get -> bool +Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.RunConfigurationSettings.RunConfigurationSettings() -> void +Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.TestRunCancellationToken +Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.TestRunCancellationToken.Cancel() -> void +Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.TestRunCancellationToken.Canceled.get -> bool +Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.TestRunCancellationToken.Register(System.Action! callback) -> void +Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.TestRunCancellationToken.TestRunCancellationToken() -> void +Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.TestRunCancellationToken.Unregister() -> void Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.AdapterTraceLogger Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.AdapterTraceLogger.AdapterTraceLogger() -> void Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.AdapterTraceLogger.LogError(string! format, params object?[]! args) -> void @@ -11,7 +139,7 @@ Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.FileOperation Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.FileOperations.FileOperations() -> void Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.FileOperations.GetAssemblyPath(System.Reflection.Assembly! assembly) -> string? Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.FileOperations.GetFullFilePath(string! assemblyFileName) -> string! -Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.FileOperations.GetNavigationData(object! navigationSession, string! className, string! methodName, out int minLineNumber, out string? fileName) -> void +Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.FileOperations.GetNavigationData(object? navigationSession, string! className, string! methodName, out int minLineNumber, out string? fileName) -> void Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.FileOperations.LoadAssembly(string! assemblyName, bool isReflectionOnly) -> System.Reflection.Assembly! Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface.IAdapterTraceLogger Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface.IAdapterTraceLogger.LogError(string! format, params object?[]! args) -> void @@ -81,7 +209,7 @@ Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface.Obj Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface.ObjectModel.ITestMethod.Name.get -> string! Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface.ObjectModel.ITestMethod.TestIdGenerationStrategy.get -> Microsoft.VisualStudio.TestTools.UnitTesting.TestIdGenerationStrategy Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.MSTestSettingsProvider -Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.MSTestSettingsProvider.GetProperties(string! source) -> System.Collections.Generic.IDictionary! +Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.MSTestSettingsProvider.GetProperties(string? source) -> System.Collections.Generic.IDictionary! Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.MSTestSettingsProvider.Load(System.Xml.XmlReader! reader) -> void Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.MSTestSettingsProvider.MSTestSettingsProvider() -> void Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.ReflectionOperations @@ -93,6 +221,7 @@ Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.TestContextIm Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.TestContextImplementation.AddProperty(string! propertyName, string! propertyValue) -> void Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.TestContextImplementation.ClearDiagnosticMessages() -> void Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.TestContextImplementation.Context.get -> Microsoft.VisualStudio.TestTools.UnitTesting.TestContext! +Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.TestContextImplementation.Dispose() -> void Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.TestContextImplementation.GetDiagnosticMessages() -> string? Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.TestContextImplementation.GetResultFiles() -> System.Collections.Generic.IList? Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.TestContextImplementation.SetDataConnection(object? dbConnection) -> void @@ -150,3 +279,14 @@ override Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Thre override Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.ThreadSafeStringWriter.Write(char[]! buffer, int index, int count) -> void override Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.ThreadSafeStringWriter.Write(string? value) -> void override Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.ThreadSafeStringWriter.WriteLine(string? value) -> void +static Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Extensions.TestResultExtensions.ToUnitTestResults(this Microsoft.VisualStudio.TestTools.UnitTesting.TestResult![]! testResults) -> Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.UnitTestResult![]! +static Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Extensions.UnitTestOutcomeExtensions.ToUnitTestOutcome(this Microsoft.VisualStudio.TestTools.UnitTesting.UnitTestOutcome frameworkTestOutcome) -> Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.UnitTestOutcome +static Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.MSTestSettings.CurrentSettings.get -> Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.MSTestSettings! +static Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.MSTestSettings.IsLegacyScenario(Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging.IMessageLogger! logger) -> bool +static Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.MSTestSettings.PopulateSettings(Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.MSTestSettings! settings) -> void +static Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.MSTestSettings.PopulateSettings(Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter.IDiscoveryContext? context) -> void +static Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.MSTestSettings.RunConfigurationSettings.get -> Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.RunConfigurationSettings! +static Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.RunConfigurationSettings.PopulateSettings(Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter.IDiscoveryContext? context) -> Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.RunConfigurationSettings! +virtual Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestMethodInfo.Invoke(object?[]? arguments) -> Microsoft.VisualStudio.TestTools.UnitTesting.TestResult! +virtual Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestMethodInfo.InvokeAsync(object?[]? arguments) -> System.Threading.Tasks.Task! +virtual Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.TestContextImplementation.Dispose(bool disposing) -> void diff --git a/src/Adapter/MSTestAdapter.PlatformServices/PublicAPI/PublicAPI.Unshipped.txt b/src/Adapter/MSTestAdapter.PlatformServices/PublicAPI/PublicAPI.Unshipped.txt index 6a828f7e91..07e3531963 100644 --- a/src/Adapter/MSTestAdapter.PlatformServices/PublicAPI/PublicAPI.Unshipped.txt +++ b/src/Adapter/MSTestAdapter.PlatformServices/PublicAPI/PublicAPI.Unshipped.txt @@ -1,5 +1,2 @@ #nullable enable -*REMOVED*Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.FileOperations.GetNavigationData(object! navigationSession, string! className, string! methodName, out int minLineNumber, out string? fileName) -> void -Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.FileOperations.GetNavigationData(object? navigationSession, string! className, string! methodName, out int minLineNumber, out string? fileName) -> void -*REMOVED*Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.MSTestSettingsProvider.GetProperties(string! source) -> System.Collections.Generic.IDictionary! -Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.MSTestSettingsProvider.GetProperties(string? source) -> System.Collections.Generic.IDictionary! +*REMOVED*virtual Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.TestContextImplementation.Dispose(bool disposing) -> void diff --git a/src/Adapter/MSTestAdapter.PlatformServices/RecursiveDirectoryPath.cs b/src/Adapter/MSTestAdapter.PlatformServices/RecursiveDirectoryPath.cs index c91bf7ed34..5593828ee9 100644 --- a/src/Adapter/MSTestAdapter.PlatformServices/RecursiveDirectoryPath.cs +++ b/src/Adapter/MSTestAdapter.PlatformServices/RecursiveDirectoryPath.cs @@ -20,9 +20,9 @@ namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices; /// [Serializable] #if NET6_0_OR_GREATER -[Obsolete(Constants.PublicTypeObsoleteMessage, DiagnosticId = "MSTESTOBS")] +[Obsolete(TestTools.UnitTesting.FrameworkConstants.PublicTypeObsoleteMessage, DiagnosticId = "MSTESTOBS")] #else -[Obsolete(Constants.PublicTypeObsoleteMessage)] +[Obsolete(TestTools.UnitTesting.FrameworkConstants.PublicTypeObsoleteMessage)] #endif [SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1603:DocumentationMustContainValidXml", Justification = "Reviewed. Suppression is ok here.")] public class RecursiveDirectoryPath : MarshalByRefObject diff --git a/src/Adapter/MSTestAdapter.PlatformServices/Resources/Resource.Designer.cs b/src/Adapter/MSTestAdapter.PlatformServices/Resources/Resource.Designer.cs deleted file mode 100644 index 33f4d030e6..0000000000 --- a/src/Adapter/MSTestAdapter.PlatformServices/Resources/Resource.Designer.cs +++ /dev/null @@ -1,271 +0,0 @@ -//------------------------------------------------------------------------------ -// -// 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.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices { - 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 Resource { - - private static global::System.Resources.ResourceManager resourceMan; - - private static global::System.Globalization.CultureInfo resourceCulture; - - [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] - internal Resource() { - } - - /// - /// 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.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Resources.Reso" + - "urce", typeof(Resource).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 Could not find file '{0}'.. - /// - internal static string CannotFindFile { - get { - return ResourceManager.GetString("CannotFindFile", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to The parameter should not be null or empty.. - /// - internal static string Common_CannotBeNullOrEmpty { - get { - return ResourceManager.GetString("Common_CannotBeNullOrEmpty", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Test Run deployment issue: Bad deployment item: '{0}': output directory '{1}' specifies the item to be deployed outside deployment root directory which is not allowed.. - /// - internal static string DeploymentErrorBadDeploymentItem { - get { - return ResourceManager.GetString("DeploymentErrorBadDeploymentItem", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Test Run deployment issue: Failed to access output directory '{1}' specified by deployment item '{0}', the item will not be deployed: {2}: {3}. - /// - internal static string DeploymentErrorFailedToAccesOutputDirectory { - get { - return ResourceManager.GetString("DeploymentErrorFailedToAccesOutputDirectory", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Test Run deployment issue: Failed to access the file '{0}': {1}: {2}. - /// - internal static string DeploymentErrorFailedToAccessFile { - get { - return ResourceManager.GetString("DeploymentErrorFailedToAccessFile", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Test Run deployment issue: Failed to copy file '{0}' to '{1}': {2}: {3}. - /// - internal static string DeploymentErrorFailedToCopyWithOverwrite { - get { - return ResourceManager.GetString("DeploymentErrorFailedToCopyWithOverwrite", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Test Run deployment issue: Failed to deploy dependencies for test storage '{0}': {1}. - /// - internal static string DeploymentErrorFailedToDeployDependencies { - get { - return ResourceManager.GetString("DeploymentErrorFailedToDeployDependencies", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Test Run deployment issue: Failed to get the file for {0}: {1}: {2}. - /// - internal static string DeploymentErrorFailedToGetFileForDeploymentItem { - get { - return ResourceManager.GetString("DeploymentErrorFailedToGetFileForDeploymentItem", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Test Run deployment issue: an error occurred while getting satellite assemblies for {0}: {1}: {2}. - /// - internal static string DeploymentErrorGettingSatellite { - get { - return ResourceManager.GetString("DeploymentErrorGettingSatellite", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to deployment item '{0}'. - /// - internal static string DeploymentItem { - get { - return ResourceManager.GetString("DeploymentItem", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Invalid deployment item: the specified path '{0}' or output directory '{1}' contains illegal characters.. - /// - internal static string DeploymentItemContainsInvalidCharacters { - get { - return ResourceManager.GetString("DeploymentItemContainsInvalidCharacters", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Invalid deployment item: the output directory cannot be null.. - /// - internal static string DeploymentItemOutputDirectoryCannotBeNull { - get { - return ResourceManager.GetString("DeploymentItemOutputDirectoryCannotBeNull", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Invalid deployment item: the specified output directory '{0}' is not relative.. - /// - internal static string DeploymentItemOutputDirectoryMustBeRelative { - get { - return ResourceManager.GetString("DeploymentItemOutputDirectoryMustBeRelative", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Invalid deployment item: the path must contain at least one character.. - /// - internal static string DeploymentItemPathCannotBeNullOrEmpty { - get { - return ResourceManager.GetString("DeploymentItemPathCannotBeNullOrEmpty", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to deployment item '{0}' (output directory '{1}'). - /// - internal static string DeploymentItemWithOutputDirectory { - get { - return ResourceManager.GetString("DeploymentItemWithOutputDirectory", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to MSTestAdapter encountered an unexpected element '{0}' in its settings '{1}'. Remove this element and try again.. - /// - internal static string InvalidSettingsXmlElement { - get { - return ResourceManager.GetString("InvalidSettingsXmlElement", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Invalid value '{0}' for runsettings entry '{1}', setting will be ignored.. - /// - internal static string InvalidValue { - get { - return ResourceManager.GetString("InvalidValue", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Test Run deployment issue: The assembly or module '{0}' was not found. Reason: {1}. - /// - internal static string MissingDeploymentDependency { - get { - return ResourceManager.GetString("MissingDeploymentDependency", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Test Run deployment issue: The assembly or module '{0}' was not found.. - /// - internal static string MissingDeploymentDependencyWithoutReason { - get { - return ResourceManager.GetString("MissingDeploymentDependencyWithoutReason", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to {0}_{1} {2}. - /// - internal static string TestRunName { - get { - return ResourceManager.GetString("TestRunName", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Data source '{0}' cannot be found in the test configuration settings. - /// - internal static string UTA_DataSourceConfigurationSectionMissing { - get { - return ResourceManager.GetString("UTA_DataSourceConfigurationSectionMissing", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to The unit test adapter failed to connect to the data source or to read the data. For more information on troubleshooting this error, see "Troubleshooting Data-Driven Unit Tests" (http://go.microsoft.com/fwlink/?LinkId=62412) in the MSDN Library. Error details: {0}. - /// - internal static string UTA_ErrorDataConnectionFailed { - get { - return ResourceManager.GetString("UTA_ErrorDataConnectionFailed", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Wrong number of objects for permutation. Should be greater than zero.. - /// - internal static string WrongNumberOfObjects { - get { - return ResourceManager.GetString("WrongNumberOfObjects", resourceCulture); - } - } - } -} diff --git a/src/Adapter/MSTestAdapter.PlatformServices/Resources/Resource.resx b/src/Adapter/MSTestAdapter.PlatformServices/Resources/Resource.resx index 414073c307..01a75aa4da 100644 --- a/src/Adapter/MSTestAdapter.PlatformServices/Resources/Resource.resx +++ b/src/Adapter/MSTestAdapter.PlatformServices/Resources/Resource.resx @@ -186,4 +186,305 @@ Invalid value '{0}' for runsettings entry '{1}', setting will be ignored. + + Assembly cleanup method '{0}.{1}' timed out after {2}ms + + + Assembly cleanup method '{0}.{1}' was canceled + + + Assembly initialize method '{0}.{1}' timed out after {2}ms + + + Assembly initialize method '{0}.{1}' was canceled + + + MSTestAdapterV2 + + + Exception occurred while enumerating IDataSource attribute on "{0}.{1}": {2} + {0}: TypeName with namespace, +{1}: Method name, +{2}: Exception details + + + Exception occurred while expanding IDataSource rows from attribute on "{0}.{1}": {2} + {0}: TypeName with namespace, +{1}: Method name, +{2}: CannotExpandIDataSourceAttribute_DuplicateDisplayName or CannotExpandIDataSourceAttribute_CannotSerialize + + + Data on index {0} for "{1}" cannot be serialized. All data provided through "IDataSource" should be serializable. If you need to test non-serializable data sources, please make sure you add "TestDataSourceDiscovery" attribute on your test assembly and set the discovery option to "DuringExecution". + {0}: Zero based index if an element inside of an array, +{1}: Test name + + + Display name "{2}" on indexes {0} and {1} are duplicate. Display names should be unique. + {0}, {1}: Zero based index if an element inside of an array +{2}: Test display name. + + + Cannot run test method '{0}.{1}': Test data doesn't match method parameters. Either the count or types are different. +Test expected {2} parameter(s), with types '{3}', +but received {4} argument(s), with types '{5}'. + + + Cannot run test method '{0}.{1}': Method has parameters, but does not define any test source. Use '[DataRow]', '[DynamicData]', or a custom 'ITestDataSource' data source to provide test data. + + + Class cleanup method '{0}.{1}' timed out after {2}ms + + + Class cleanup method '{0}.{1}' was canceled + + + Class initialize method '{0}.{1}' timed out after {2}ms + + + Class initialize method '{0}.{1}' was canceled + + + MSTestAdapter failed to discover tests in class '{0}' of assembly '{1}' because {2}. + + + {0} (Data Row {1}) + + + Debug Trace: + + + [MSTest][Discovery][{0}] {1} + + + Both '.runsettings' and '.testconfig.json' files have been detected. Please select only one of these test configuration files. + + + {0}: {1} + + + "{0}": (Failed to get exception description due to an exception of type "{1}". + {0}: Type of the original exception that we're trying to get the description of. +{1}: Thrown exception + + + Exceptions thrown: + This is usually precedes by TestAssembly_AssemblyDiscoveryFailure message, and preceded by list of exceptions thrown in a test discovery session. + + + Test '{0}' was canceled + + + Test '{0}' timed out after {1}ms + + + Getting custom attributes for type {0} threw exception (will ignore and use the reflection way): {1} + {0}: Attribute full type name. +{1}: Exception description + + + The type of the generic parameter '{0}' could not be inferred. + + + The generic test method '{0}' doesn't have arguments, so the generic parameter cannot be inferred. + + + Found two conflicting types for generic parameter '{0}'. The conflicting types are '{1}' and '{2}'. + + + Invalid value '{0}' specified for 'ClassCleanupLifecycle'. Supported scopes are {1}. + {Locked="ClassCleanupLifecycle"} + + + Invalid value '{0}' specified for 'Scope'. Supported scopes are {1}. + {Locked="Scope"} + + + Invalid value '{0}' specified for 'Workers'. The value should be a non-negative integer. + {Locked="Workers"} + + + Invalid settings '{0}'. Unexpected XmlAttribute: '{1}'. + + + Invalid settings '{0}'. Unexpected XmlElement: '{1}'. + + + Invalid value '{0}' for runsettings entry '{1}', setting will be ignored. + + + Warning : A testsettings file or a vsmdi file is not supported with the MSTest V2 Adapter. + + + An older version of MSTestV2 package is loaded in assembly, test discovery might fail to discover all data tests if they depend on `.runsettings` file. + + + Running tests in any of the provided sources is not supported for the selected platform + + + Failed to discover tests from assembly {0}. Reason:{1} + + + File does not exist: {0} + + + Test cleanup method '{0}.{1}' timed out after {2}ms + + + Test cleanup method '{0}.{1}' was canceled + + + TestContext cannot be Null. + + + TestContext Messages: + + + Test initialize method '{0}.{1}' timed out after {2}ms + + + Test initialize method '{0}.{1}' was canceled + + + Test method {0} was not found. + + + Test Parallelization enabled for {0} (Workers: {1}, Scope: {2}) + {Locked="Workers"}{Locked="Scope"} + + + Unable to load types from the test source '{0}'. Some or all of the tests in this source may not be discovered. +Error: {1} + + + Assembly Cleanup method {0}.{1} failed. Error Message: {2}. StackTrace: {3} + + + Assembly Initialization method {0}.{1} threw exception. {2}: {3}. Aborting test execution. + + + Class Cleanup method {0}.{1} failed. Error Message: {2}. Stack Trace: {3} + + + Class Initialization method {0}.{1} threw exception. {2}: {3}. + + + Method {0}.{1} has wrong signature. The method must be static, public, does not return a value and should not take any parameter. Additionally, if you are using async-await in method then return-type must be 'Task' or 'ValueTask'. + + + Method {0}.{1} has wrong signature. The method must be static, public, does not return a value and should take a single parameter of type TestContext. Additionally, if you are using async-await in method then return-type must be 'Task' or 'ValueTask'. + + + TestCleanup method {0}.{1} threw exception. {2}. + + + Error calling Test Cleanup method for test class {0}: {1} + + + TestCleanup Stack Trace + + + --- End of inner exception stack trace --- + + + UTA007: Method {1} defined in class {0} does not have correct signature. Test method marked with the [TestMethod] attribute must be non-static, public, return-type as void and should not take any parameter. Example: public void Test.Class1.Test(). Additionally, if you are using async-await in test method then return-type must be 'Task' or 'ValueTask'. Example: public async Task Test.Class1.Test2() + + + UTA031: class {0} does not have valid TestContext property. TestContext must be of type TestContext, must be non-static, and must be public. For example: public TestContext TestContext. + + + UTA054: {0}.{1} has invalid Timeout attribute. The timeout must be an integer value greater than 0. + + + UTA014: {0}: Cannot define more than one method with the AssemblyCleanup attribute inside an assembly. + + + UTA013: {0}: Cannot define more than one method with the AssemblyInitialize attribute inside an assembly. + + + UTA026: {0}: Cannot define more than one method with the ClassCleanup attribute inside a class. + + + UTA025: {0}: Cannot define more than one method with the ClassInitialize attribute inside a class. + + + UTA024: {0}: Cannot define more than one method with the TestCleanup attribute. + + + UTA018: {0}: Cannot define more than one method with the TestInitialize attribute. + + + UTA001: TestClass attribute defined on non-public class {0} + + + UTA023: {0}: Cannot define predefined property {2} on method {1}. + + + TestClass attribute defined on generic non-abstract class {0} + + + UTA021: {0}: Null or empty custom property defined on method {1}. The custom property must have a valid name. + + + An unhandled exception was thrown by the 'Execute' method. Please report this error to the author of the attribute '{0}'. +{1} + + + The ExpectedException attribute defined on test method {0}.{1} threw an exception during construction. +{2} + + + Failed to obtain the exception thrown by test method {0}.{1}. + + + Initialization method {0}.{1} threw exception. {2}. + + + Unable to create instance of class {0}. Error: {1}. + + + Method {0}.{1} does not exist. + + + The test method '{0}.{1}' has multiple attributes derived from '{2}' defined on it. Only one such attribute is allowed. + + + Error in executing test. No result returned by extension. If using extension of TestMethodAttribute then please contact vendor. + + + Cannot find a valid constructor for test class '{0}'. Valid constructors are 'public' and either parameterless or with one parameter of type 'TestContext'. + + + Unable to find property {0}.TestContext. Error:{1}. + + + Unable to set TestContext property for the class {0}. Error: {1}. + + + The {0}.TestContext has incorrect type. + + + Method {0}.{1} has wrong signature. The method must be non-static, public, does not return a value and should not take any parameter. Additionally, if you are using async-await in method then return-type must be 'Task' or 'ValueTask'. + + + Test method {0}.{1} threw exception: +{2} + + + Unable to get type {0}. Error: {1}. + + + The called code threw an exception that was caught, but the exception value was null + + + {0} For UWP projects, if you are using UI objects in test consider using [UITestMethod] attribute instead of [TestMethod] to execute test in UI thread. + + + (Failed to get the message for an exception of type {0} due to an exception.) + + + Runsettings entry '<ExecutionApartmentState>STA</ExecutionApartmentState>' is not supported on non-Windows OSes. + + + 'MSTest.TestAdapter' and 'MSTest.TestFramework' must have the same version. Found 'MSTest.TestAdapter' version '{0}' and 'MSTest.TestFramework' version '{1}'. Please make sure that the versions of 'MSTest.TestAdapter' and 'MSTest.TestFramework' NuGet packages have the same version. + \ No newline at end of file diff --git a/src/Adapter/MSTestAdapter.PlatformServices/Resources/xlf/Resource.cs.xlf b/src/Adapter/MSTestAdapter.PlatformServices/Resources/xlf/Resource.cs.xlf index b82c787000..b8b854815b 100644 --- a/src/Adapter/MSTestAdapter.PlatformServices/Resources/xlf/Resource.cs.xlf +++ b/src/Adapter/MSTestAdapter.PlatformServices/Resources/xlf/Resource.cs.xlf @@ -2,16 +2,116 @@ + + Assembly cleanup method '{0}.{1}' timed out after {2}ms + Po {2} ms vypršel časový limit metody čištění sestavení {0}.{1}. + + + + Assembly cleanup method '{0}.{1}' was canceled + Metoda čištění sestavení {0}.{1} byla zrušena. + + + + Assembly initialize method '{0}.{1}' timed out after {2}ms + Po {2} ms vypršel časový limit metody inicializace sestavení {0}.{1}. + + + + Assembly initialize method '{0}.{1}' was canceled + Metoda inicializace sestavení {0}.{1} byla zrušena. + + + + MSTestAdapterV2 + MSTestAdapterV2 + + + + Exception occurred while enumerating IDataSource attribute on "{0}.{1}": {2} + Při vytváření výčtu atributu IDataSource došlo k výjimce. „{0}.{1}“: {2} + {0}: TypeName with namespace, +{1}: Method name, +{2}: Exception details + + + Exception occurred while expanding IDataSource rows from attribute on "{0}.{1}": {2} + Došlo k výjimce při rozbalování řádků IDataSource z atributu na „{0}.{1}“: {2} + {0}: TypeName with namespace, +{1}: Method name, +{2}: CannotExpandIDataSourceAttribute_DuplicateDisplayName or CannotExpandIDataSourceAttribute_CannotSerialize + + + Data on index {0} for "{1}" cannot be serialized. All data provided through "IDataSource" should be serializable. If you need to test non-serializable data sources, please make sure you add "TestDataSourceDiscovery" attribute on your test assembly and set the discovery option to "DuringExecution". + Data v {0} indexu pro „{1}“ nelze serializovat. Všechna data poskytnutá prostřednictvím „IDataSource“ by měla být serializovatelná. Pokud potřebujete testovat neserializovatelné zdroje dat, nezapomeňte do testovacího sestavení přidat atribut TestDataSourceDiscovery a nastavit možnost zjišťování na „DuringExecution“. + {0}: Zero based index if an element inside of an array, +{1}: Test name + + + Display name "{2}" on indexes {0} and {1} are duplicate. Display names should be unique. + Zobrazovaný název „{2}“ u indexů {0} a {1} je duplicitní. Zobrazované názvy by měly být jedinečné. + {0}, {1}: Zero based index if an element inside of an array +{2}: Test display name. + Could not find file '{0}'. Soubor {0} se nenašel. + + Cannot run test method '{0}.{1}': Test data doesn't match method parameters. Either the count or types are different. +Test expected {2} parameter(s), with types '{3}', +but received {4} argument(s), with types '{5}'. + Testovací metodu {0}.{1} nejde spustit: Testovací data neodpovídají parametrům metody. Liší se počet nebo typy. +Pro test se očekával tento počet parametrů: {2} s typy {3}, +byl však přijat tento počet argumentů: {4} s typy {5}. + + + + Cannot run test method '{0}.{1}': Method has parameters, but does not define any test source. Use '[DataRow]', '[DynamicData]', or a custom 'ITestDataSource' data source to provide test data. + Nelze spustit testovací metodu {0}.{1}: Metoda má parametry, ale nedefinuje žádný zdroj testu. K poskytování testovacích dat použijte zdroj dat [DataRow] nebo [DynamicData], případně vlastní zdroj dat ITestDataSource. + + + + 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}. + + + + Class cleanup method '{0}.{1}' was canceled + Metoda čištění třídy {0}.{1} byla zrušena. + + + + Class initialize method '{0}.{1}' timed out after {2}ms + Po {2} ms vypršel časový limit metody inicializace třídy {0}.{1}. + + + + Class initialize method '{0}.{1}' was canceled + Metoda inicializace třídy {0}.{1} byla zrušena. + + The parameter should not be null or empty. Parametr nemůže být null nebo prázdný. + + MSTestAdapter failed to discover tests in class '{0}' of assembly '{1}' because {2}. + MSTestAdapter nezjistil v třídě {0} sestavení {1} žádný test, protože: {2}. + + + + {0} (Data Row {1}) + {0} (datový řádek {1}) + + + + Debug Trace: + Trasování ladění: + + Test Run deployment issue: Bad deployment item: '{0}': output directory '{1}' specifies the item to be deployed outside deployment root directory which is not allowed. Problém nasazení testovacího běhu: Chybná položka nasazení: {0}: výstupní adresář {1} určuje položku, která se má nasadit mimo kořenový adresář nasazení, což není povolené. @@ -77,16 +177,108 @@ položka nasazení {0} (adresář výstupu {1}) + + [MSTest][Discovery][{0}] {1} + [MSTest][Discovery][{0}] {1} + + + + Both '.runsettings' and '.testconfig.json' files have been detected. Please select only one of these test configuration files. + Byly zjištěny soubory .runsettings i .testconfig.json. Vyberte prosím jenom jeden z těchto souborů konfigurace testu. + + + + {0}: {1} + {0}: {1} + + + + "{0}": (Failed to get exception description due to an exception of type "{1}". + „{0}“: (Nepodařilo se získat popis výjimky z důvodu výjimky typu „{1}“. + {0}: Type of the original exception that we're trying to get the description of. +{1}: Thrown exception + + + Exceptions thrown: + Vyvolané výjimky: + This is usually precedes by TestAssembly_AssemblyDiscoveryFailure message, and preceded by list of exceptions thrown in a test discovery session. + + + Test '{0}' was canceled + Test {0} byl zrušen. + + + + Test '{0}' timed out after {1}ms + Časový limit testu {0} vypršel po {1} ms. + + + + Getting custom attributes for type {0} threw exception (will ignore and use the reflection way): {1} + Získání vlastních atributů pro typ {0} vyvolalo výjimku (bude ignorovat a používat způsob reflexe): {1} + {0}: Attribute full type name. +{1}: Exception description + + + The type of the generic parameter '{0}' could not be inferred. + Typ obecného parametru {0} nelze odvodit. + + + + The generic test method '{0}' doesn't have arguments, so the generic parameter cannot be inferred. + Obecná testovací metoda {0}'nemá argumenty, takže nelze odvodit obecný parametr. + + + + Found two conflicting types for generic parameter '{0}'. The conflicting types are '{1}' and '{2}'. + Byly nalezeny dva konfliktní typy pro obecný parametr {0}. Konfliktní typy jsou {1} a {2}. + + + + Invalid value '{0}' specified for 'ClassCleanupLifecycle'. Supported scopes are {1}. + Pro 'ClassCleanupLifecycle' byla zadána neplatná hodnota {0}. Podporované obory jsou {1}. + {Locked="ClassCleanupLifecycle"} + + + Invalid value '{0}' specified for 'Scope'. Supported scopes are {1}. + Pro procesy 'Scope' je zadaná neplatná hodnota {0}. Podporované obory jsou {1}. + {Locked="Scope"} + + + Invalid value '{0}' specified for 'Workers'. The value should be a non-negative integer. + Pro procesy 'Workers' je zadaná neplatná hodnota {0}. Hodnota by měla být nezáporné celé číslo. + {Locked="Workers"} + + + Invalid settings '{0}'. Unexpected XmlAttribute: '{1}'. + Neplatné nastavení {0}. Neočekávaný XmlAttribute: {1}. + + MSTestAdapter encountered an unexpected element '{0}' in its settings '{1}'. Remove this element and try again. Nástroj MSTestAdapter zjistil neočekávaný prvek {0} v nastavení {1}. Odeberte tento prvek a zkuste to znovu. + + Invalid settings '{0}'. Unexpected XmlElement: '{1}'. + Neplatné nastavení {0}. Neočekávaný XmlElement: {1}. + + Invalid value '{0}' for runsettings entry '{1}', setting will be ignored. Neplatná hodnota {0} pro položku runsettings {1}. Nastavení bude ignorováno. + + Invalid value '{0}' for runsettings entry '{1}', setting will be ignored. + Neplatná hodnota {0} pro položku runsettings {1}. Nastavení bude ignorováno. + + + + Warning : A testsettings file or a vsmdi file is not supported with the MSTest V2 Adapter. + Upozornění: Adaptér MSTest V2 nepodporuje soubor testsettings ani vsmdi. + + Test Run deployment issue: The assembly or module '{0}' was not found. Reason: {1} Problém nasazení testovacího běhu: Sestavení nebo modul {0} se nenašly. Důvod: {1} @@ -97,21 +289,309 @@ Problém nasazení testovacího běhu: Sestavení nebo modul {0} se nenašly. + + An older version of MSTestV2 package is loaded in assembly, test discovery might fail to discover all data tests if they depend on `.runsettings` file. + V sestavení je načtena starší verze balíčku MSTestV2. Zjišťování testů může selhat při zjišťování všech testů dat, pokud jsou závislé na souboru .runsettings. + + + + Runsettings entry '<ExecutionApartmentState>STA</ExecutionApartmentState>' is not supported on non-Windows OSes. + Položka runsettings <ExecutionApartmentState>STA</ExecutionApartmentState> se v operačních systémech jiných než Windows nepodporuje. + + + + Running tests in any of the provided sources is not supported for the selected platform + Spouštění testů v některém z uvedených zdrojů se pro vybranou platformu nepodporuje. + + + + Failed to discover tests from assembly {0}. Reason:{1} + Nepovedlo se zjistit testy ze sestavení {0}. Důvod:{1} + + + + File does not exist: {0} + Neexistující soubor: {0} + + + + Test cleanup method '{0}.{1}' timed out after {2}ms + Po {2} ms vypršel časový limit metody čištění testu {0}.{1}. + + + + Test cleanup method '{0}.{1}' was canceled + Metoda čištění testu {0}.{1} byla zrušena. + + + + TestContext cannot be Null. + TestContext nemůže být Null. + + + + TestContext Messages: + Zprávy pro TestContext: + + + + Test initialize method '{0}.{1}' timed out after {2}ms + Po {2} ms vypršel časový limit metody inicializace testu {0}.{1}. + + + + Test initialize method '{0}.{1}' was canceled + Metoda inicializace testu {0}.{1} byla zrušena. + + + + Test method {0} was not found. + Testovací metoda {0} se nenašla. + + + + Test Parallelization enabled for {0} (Workers: {1}, Scope: {2}) + Je povolená paralelizace testu pro {0} (Workers: {1}, Scope: {2}) + {Locked="Workers"}{Locked="Scope"} + {0}_{1} {2} {0}_{1} {2} + + Unable to load types from the test source '{0}'. Some or all of the tests in this source may not be discovered. +Error: {1} + Nepovedlo se načíst typy ze zdroje testu {0}. Je možné, že se některé nebo všechny testy v tomto zdroji nezjistily. +Chyba: {1} + + + + Assembly Cleanup method {0}.{1} failed. Error Message: {2}. StackTrace: {3} + Čisticí metoda sestavení {0}.{1} selhala. Chybová zpráva: {2}. Trasování zásobníku: {3} + + + + Assembly Initialization method {0}.{1} threw exception. {2}: {3}. Aborting test execution. + Inicializační metoda sestavení {0}.{1} vyvolala výjimku. {2}: {3}. Přerušuje se provádění testu. + + + + Class Cleanup method {0}.{1} failed. Error Message: {2}. Stack Trace: {3} + Čisticí metoda třídy {0}.{1} selhala. Chybová zpráva: {2}. Trasování zásobníku: {3} + + + + Class Initialization method {0}.{1} threw exception. {2}: {3}. + Inicializační metoda třídy {0}.{1} vyvolala výjimku. {2}: {3}. + + + + Method {0}.{1} has wrong signature. The method must be static, public, does not return a value and should not take any parameter. Additionally, if you are using async-await in method then return-type must be 'Task' or 'ValueTask'. + Metoda {0}.{1} má špatný podpis. Metoda musí být static nebo public, nevrací hodnotu a nesmí přijímat žádný parametr. Pokud navíc v metodě používáte operátor async-await, musí být návratový typ Task nebo ValueTask. + + + + Method {0}.{1} has wrong signature. The method must be static, public, does not return a value and should take a single parameter of type TestContext. Additionally, if you are using async-await in method then return-type must be 'Task' or 'ValueTask'. + Metoda {0}.{1} má špatný podpis. Metoda musí být static nebo public, nevrací hodnotu a musí přijímat jeden parametr typu TestContext. Pokud navíc v metodě používáte operátor async-await, musí být návratový typ Task nebo ValueTask. + + + + TestCleanup method {0}.{1} threw exception. {2}. + Metoda TestCleanup {0}.{1} vyvolala výjimku. {2}. + + + + Error calling Test Cleanup method for test class {0}: {1} + Při volání čisticí metody testu pro třídu {0} došlo k chybě: {1} + + + + TestCleanup Stack Trace + Trasování zásobníku čištění testu + + Data source '{0}' cannot be found in the test configuration settings Zdroj dat {0} se v nastavení konfigurace testu nenašel. + + --- End of inner exception stack trace --- + --- Konec trasování zásobníku pro vnitřní výjimku --- + + The unit test adapter failed to connect to the data source or to read the data. For more information on troubleshooting this error, see "Troubleshooting Data-Driven Unit Tests" (http://go.microsoft.com/fwlink/?LinkId=62412) in the MSDN Library. Error details: {0} Adaptéru testování částí se buď nepodařilo připojit ke zdroji dat, nebo přečíst data. Další informace o odstraňování této chyby najdete v knihovně MSDN v tématu Troubleshooting Data-Driven Unit Tests (http://go.microsoft.com/fwlink/?LinkId=62412). Podrobnosti o chybě: {0} + + UTA031: class {0} does not have valid TestContext property. TestContext must be of type TestContext, must be non-static, and must be public. For example: public TestContext TestContext. + UTA031: Třída {0} nemá platnou vlastnost TestContext. Vlastnost TestContext musí být typu TestContext, musí být nestatická a musí být veřejná. Například: public TestContext TestContext + + + + UTA007: Method {1} defined in class {0} does not have correct signature. Test method marked with the [TestMethod] attribute must be non-static, public, return-type as void and should not take any parameter. Example: public void Test.Class1.Test(). Additionally, if you are using async-await in test method then return-type must be 'Task' or 'ValueTask'. Example: public async Task Test.Class1.Test2() + UTA007: Metoda {1} definovaná ve třídě {0} nemá správný podpis. Testovací metoda označená atributem [TestMethod] nesmí být static ani public, musí mít návratový typ void a nesmí přijímat žádný parametr. Například: public void Test.Class1.Test(). Pokud navíc v testovací metodě používáte operátor async-await, musí být návratový typ Task nebo ValueTask. Například: public async Task Test.Class1.Test2() + + + + UTA054: {0}.{1} has invalid Timeout attribute. The timeout must be an integer value greater than 0. + UTA054: {0}.{1} má neplatný atribut Timeout. Hodnota timeline musí být celé číslo větší než 0. + + + + UTA014: {0}: Cannot define more than one method with the AssemblyCleanup attribute inside an assembly. + UTA014: {0}: V jednom sestavení nejde definovat více jak jednu metodu s atributem AssemblyCleanup. + + + + UTA013: {0}: Cannot define more than one method with the AssemblyInitialize attribute inside an assembly. + UTA013: {0}: V jednom sestavení nejde definovat více jak jednu metodu s atributem AssemblyInitialize. + + + + UTA026: {0}: Cannot define more than one method with the ClassCleanup attribute inside a class. + UTA026: {0}: Uvnitř třídy nejde definovat více jak jednu metodu s atributem ClassCleanup. + + + + UTA025: {0}: Cannot define more than one method with the ClassInitialize attribute inside a class. + UTA025: {0}: Uvnitř třídy nejde definovat více jak jednu metodu s atributem ClassInitialize. + + + + UTA024: {0}: Cannot define more than one method with the TestCleanup attribute. + UTA024: {0}: Nejde definovat více jak jednu metodu s atributem TestCleanup. + + + + UTA018: {0}: Cannot define more than one method with the TestInitialize attribute. + UTA018: {0}: Nejde definovat více jak jednu metodu s atributem TestInitialize. + + + + UTA001: TestClass attribute defined on non-public class {0} + UTA001: Atribut TestClass se definoval v neveřejné třídě {0}. + + + + UTA023: {0}: Cannot define predefined property {2} on method {1}. + UTA023: {0}: V metodě {1} nejde definovat předdefinovanou vlastnost {2}. + + + + TestClass attribute defined on generic non-abstract class {0} + Atribut TestClass definovaný u obecné neabstraktní třídy {0} + + + + UTA021: {0}: Null or empty custom property defined on method {1}. The custom property must have a valid name. + UTA021: {0}: V metodě {1} je definovaná vlastní vlastnost, která je null nebo je prázdná. Vlastní vlastnost musí mít platný název. + + + + An unhandled exception was thrown by the 'Execute' method. Please report this error to the author of the attribute '{0}'. +{1} + Metoda Execute vyvolala neošetřenou výjimku. Nahlaste prosím tuto chybu autorovi atributu {0}. +{1} + + + + The ExpectedException attribute defined on test method {0}.{1} threw an exception during construction. +{2} + Atribut ExpectedException definovaný u testovací metody {0}.{1} vyvolal během vytváření výjimku. +{2} + + + + Failed to obtain the exception thrown by test method {0}.{1}. + Nepovedlo se získat výjimku vyvolanou testovací metodou {0}.{1}. + + + + Initialization method {0}.{1} threw exception. {2}. + Inicializační metoda {0}.{1} způsobila výjimku. {2}. + + + + Unable to create instance of class {0}. Error: {1}. + Nepodařilo se vytvořit instanci třídy {0}. Chyba: {1}. + + + + Method {0}.{1} does not exist. + Metoda {0}.{1} neexistuje. + + + + The test method '{0}.{1}' has multiple attributes derived from '{2}' defined on it. Only one such attribute is allowed. + Testovací metoda {0}.{1} má definovaných více atributů odvozených od atributu {2}. Povolený je jenom jeden takový atribut. + + + + Error in executing test. No result returned by extension. If using extension of TestMethodAttribute then please contact vendor. + Při provádění testu došlo k chybě. Rozšíření nevrátilo žádný výsledek. Pokud používáte rozšíření třídy TestMethodAttribute, obraťte se na dodavatele. + + + + Cannot find a valid constructor for test class '{0}'. Valid constructors are 'public' and either parameterless or with one parameter of type 'TestContext'. + Nelze najít platný konstruktor pro testovací třídu {0}. Platné konstruktory jsou public a buď bez parametrů, nebo s jedním parametrem typu TestContext. + + + + Unable to find property {0}.TestContext. Error:{1}. + Nepodařilo se najít vlastnost {0}.TestContext. Chyba:{1}. + + + + Unable to set TestContext property for the class {0}. Error: {1}. + Pro třídu {0} se nepodařilo nastavit vlastnost TestContext. Chyba: {1}. + + + + The {0}.TestContext has incorrect type. + {0}.TestContext má nesprávný typ. + + + + Method {0}.{1} has wrong signature. The method must be non-static, public, does not return a value and should not take any parameter. Additionally, if you are using async-await in method then return-type must be 'Task' or 'ValueTask'. + Metoda {0}.{1} má špatný podpis. Metoda nesmí být static nebo public, nevrací hodnotu a nesmí přijímat žádný parametr. Pokud navíc v metodě používáte operátor async-await, musí být návratový typ Task nebo ValueTask. + + + + Test method {0}.{1} threw exception: +{2} + V testovací metodě {0}.{1} došlo k výjimce: +{2} + + + + Unable to get type {0}. Error: {1}. + Nepodařilo se získat typ {0}. Chyba: {1}. + + + + The called code threw an exception that was caught, but the exception value was null + Volaný kód vyvolal výjimku, která byla zachycena, ale její hodnota byla null. + + + + {0} For UWP projects, if you are using UI objects in test consider using [UITestMethod] attribute instead of [TestMethod] to execute test in UI thread. + {0}. Pokud v testu používáte objekty uživatelského rozhraní, zvažte u projektů pro platformu UPW použití atributu [UITestMethod] místo atributu [TestMethod], aby se test provedl ve vlákně uživatelského rozhraní. + + + + (Failed to get the message for an exception of type {0} due to an exception.) + (Z důvodu výjimky se nepodařilo získat zprávu o výjimce typu {0}.) + + + + 'MSTest.TestAdapter' and 'MSTest.TestFramework' must have the same version. Found 'MSTest.TestAdapter' version '{0}' and 'MSTest.TestFramework' version '{1}'. Please make sure that the versions of 'MSTest.TestAdapter' and 'MSTest.TestFramework' NuGet packages have the same version. + MSTest.TestAdapter a MSTest.TestFramework musí mít stejnou verzi. Byla nalezena verze MSTest.TestAdapter {0} a verze MSTest.TestFramework {1}. Ujistěte se prosím, že verze balíčků NuGet MSTest.TestAdapter a MSTest.TestFramework jsou shodné. + + Wrong number of objects for permutation. Should be greater than zero. Chybný počet objektů pro permutaci. Počet musí být větší než nula. diff --git a/src/Adapter/MSTestAdapter.PlatformServices/Resources/xlf/Resource.de.xlf b/src/Adapter/MSTestAdapter.PlatformServices/Resources/xlf/Resource.de.xlf index a2fe24f8c5..34d0461b6d 100644 --- a/src/Adapter/MSTestAdapter.PlatformServices/Resources/xlf/Resource.de.xlf +++ b/src/Adapter/MSTestAdapter.PlatformServices/Resources/xlf/Resource.de.xlf @@ -2,16 +2,116 @@ + + Assembly cleanup method '{0}.{1}' timed out after {2}ms + Timeout der Assemblybereinigungsmethode "{0}.{1}" nach {2} ms + + + + Assembly cleanup method '{0}.{1}' was canceled + Die Assemblybereinigungsmethode "{0}.{1}" wurde abgebrochen + + + + Assembly initialize method '{0}.{1}' timed out after {2}ms + Timeout der Assemblyinitialisierungsmethode "{0}.{1}" nach {2} ms + + + + Assembly initialize method '{0}.{1}' was canceled + Die Assemblyinitialisierungsmethode "{0}.{1}" wurde abgebrochen + + + + MSTestAdapterV2 + MSTestAdapterV2 + + + + Exception occurred while enumerating IDataSource attribute on "{0}.{1}": {2} + Ausnahme beim Auflisten des IDataSource-Attributs für "{0}.{1}": {2} + {0}: TypeName with namespace, +{1}: Method name, +{2}: Exception details + + + Exception occurred while expanding IDataSource rows from attribute on "{0}.{1}": {2} + Ausnahme beim Erweitern von IDataSource-Zeilen aus dem Attribut auf "{0}.{1}": {2} + {0}: TypeName with namespace, +{1}: Method name, +{2}: CannotExpandIDataSourceAttribute_DuplicateDisplayName or CannotExpandIDataSourceAttribute_CannotSerialize + + + Data on index {0} for "{1}" cannot be serialized. All data provided through "IDataSource" should be serializable. If you need to test non-serializable data sources, please make sure you add "TestDataSourceDiscovery" attribute on your test assembly and set the discovery option to "DuringExecution". + Daten im Index {0} für "{1}" können nicht serialisiert werden. Alle über "IDataSource" bereitgestellten Daten sollten serialisierbar sein. Wenn Sie nicht serialisierbare Datenquellen testen müssen, stellen Sie sicher, dass Sie der Testassembly das Attribut "TestDataSourceDiscovery" hinzufügen und die Ermittlungsoption auf "DuringExecution" festlegen. + {0}: Zero based index if an element inside of an array, +{1}: Test name + + + Display name "{2}" on indexes {0} and {1} are duplicate. Display names should be unique. + Der Anzeigename "{2}" für Indizes {0} und {1} ist doppelt vorhanden. Anzeigenamen sollten eindeutig sein. + {0}, {1}: Zero based index if an element inside of an array +{2}: Test display name. + Could not find file '{0}'. Die Datei "{0}" konnte nicht gefunden werden. + + Cannot run test method '{0}.{1}': Test data doesn't match method parameters. Either the count or types are different. +Test expected {2} parameter(s), with types '{3}', +but received {4} argument(s), with types '{5}'. + Die Testmethode „{0}.{1}“ kann nicht ausgeführt werden: Testdaten stimmen nicht mit Methodenparametern überein. Die Anzahl oder die Typen unterscheiden sich. +Test erwartete {2} Parameter mit den Typen „{3}“, +aber empfing {4} Argument(e) mit den Typen „{5}“. + + + + Cannot run test method '{0}.{1}': Method has parameters, but does not define any test source. Use '[DataRow]', '[DynamicData]', or a custom 'ITestDataSource' data source to provide test data. + Die Testmethode „{0}.{1}“ kann nicht ausgeführt werden: Die Methode verfügt über Parameter, definiert jedoch keine Testquelle. Verwenden Sie „[DataRow]“, „[DynamicData]“ oder eine benutzerdefinierte „ITestDataSource-Datenquelle“, um Testdaten bereitzustellen. + + + + Class cleanup method '{0}.{1}' timed out after {2}ms + Timeout der Klassenbereinigungsmethode "{0}.{1}" nach {2} ms + + + + Class cleanup method '{0}.{1}' was canceled + Die Klassenbereinigungsmethode "{0}.{1}" wurde abgebrochen + + + + Class initialize method '{0}.{1}' timed out after {2}ms + Timeout der Klasseninitialisierungsmethode "{0}.{1}" nach {2} ms + + + + Class initialize method '{0}.{1}' was canceled + Die Initialisierungsmethode "{0}.{1}" der Klasse wurde abgebrochen + + The parameter should not be null or empty. Der Parameter darf nicht NULL oder leer sein. + + MSTestAdapter failed to discover tests in class '{0}' of assembly '{1}' because {2}. + Fehler von 'MSTestAdapter' beim Ermitteln von Tests in der Klasse "{0}" der Assembly "{1}". Ursache: {2}. + + + + {0} (Data Row {1}) + {0} (Datenzeile {1}) + + + + Debug Trace: + Debugablaufverfolgung: + + Test Run deployment issue: Bad deployment item: '{0}': output directory '{1}' specifies the item to be deployed outside deployment root directory which is not allowed. Problem bei der Testlaufbereitstellung: Ungültiges Bereitstellungselement: "{0}". Das Ausgabeverzeichnis "{1}" gibt das unzulässige Element an, das außerhalb des Bereitstellungsstammverzeichnisses bereitgestellt werden soll. @@ -77,16 +177,108 @@ Bereitstellungselement "{0}" (Ausgabeverzeichnis "{1}") + + [MSTest][Discovery][{0}] {1} + [MSTest][Discovery][{0}] {1} + + + + Both '.runsettings' and '.testconfig.json' files have been detected. Please select only one of these test configuration files. + Es wurden sowohl die Dateien „.runsettings“ als auch „.testconfig.json“ erkannt. Wählen Sie nur eine dieser Testkonfigurationsdateien aus. + + + + {0}: {1} + {0}: {1} + + + + "{0}": (Failed to get exception description due to an exception of type "{1}". + "{0}": (Fehler beim Abrufen der Ausnahmebeschreibung aufgrund einer Ausnahme vom Typ "{1}". + {0}: Type of the original exception that we're trying to get the description of. +{1}: Thrown exception + + + Exceptions thrown: + Ausgelöste Ausnahmen: + This is usually precedes by TestAssembly_AssemblyDiscoveryFailure message, and preceded by list of exceptions thrown in a test discovery session. + + + Test '{0}' was canceled + Test „{0}“ wurde abgebrochen + + + + Test '{0}' timed out after {1}ms + Timeout bei Test „{0}“ nach {1} ms + + + + Getting custom attributes for type {0} threw exception (will ignore and use the reflection way): {1} + Beim Abrufen von benutzerdefinierten Attributen für den Typ {0} wurde Ausnahme ausgelöst (wird ignoriert und die Reflektionsart verwendet): {1} + {0}: Attribute full type name. +{1}: Exception description + + + The type of the generic parameter '{0}' could not be inferred. + Der Typ des generischen Parameters „{0}“ konnte nicht abgeleitet werden. + + + + The generic test method '{0}' doesn't have arguments, so the generic parameter cannot be inferred. + Die generische Testmethode „{0}“ hat keine Argumente, daher kann der generische Parameter nicht abgeleitet werden. + + + + Found two conflicting types for generic parameter '{0}'. The conflicting types are '{1}' and '{2}'. + Es wurden zwei in Konflikt stehende Typen für den generischen Parameter „{0}“ gefunden. Die in Konflikt stehenden Typen sind „{1}“ und „{2}“. + + + + Invalid value '{0}' specified for 'ClassCleanupLifecycle'. Supported scopes are {1}. + Für "ClassCleanupLifecycle" wurde ein ungültiger Wert "{0}" angegeben. Unterstützte Bereiche sind {1}. + {Locked="ClassCleanupLifecycle"} + + + Invalid value '{0}' specified for 'Scope'. Supported scopes are {1}. + Ungültiger Wert "{0}" für "Scope" angegeben. Unterstützte Bereiche: {1}. + {Locked="Scope"} + + + Invalid value '{0}' specified for 'Workers'. The value should be a non-negative integer. + Ungültiger Wert "{0}" für "Workers" angegeben. Der Wert muss eine nicht negative Ganzzahl sein. + {Locked="Workers"} + + + Invalid settings '{0}'. Unexpected XmlAttribute: '{1}'. + Ungültige Einstellungen "{0}". Unerwartetes XmlAttribute: "{1}". + + MSTestAdapter encountered an unexpected element '{0}' in its settings '{1}'. Remove this element and try again. "MSTestAdapter" hat ein unerwartetes Element "{0}" in den Einstellungen "{1}" ermittelt. Entfernen Sie dieses Element, und versuchen Sie es erneut. + + Invalid settings '{0}'. Unexpected XmlElement: '{1}'. + Ungültige Einstellungen "{0}". Unerwartetes XmlElement: "{1}". + + Invalid value '{0}' for runsettings entry '{1}', setting will be ignored. Ungültiger Wert "{0}" für runsettings-Eintrag "{1}". Die Einstellung wird ignoriert. + + Invalid value '{0}' for runsettings entry '{1}', setting will be ignored. + Ungültiger Wert "{0}" für runsettings-Eintrag "{1}". Die Einstellung wird ignoriert. + + + + Warning : A testsettings file or a vsmdi file is not supported with the MSTest V2 Adapter. + Warnung: Eine TESTSETTINGS-Datei oder eine VSMDI-Datei wird vom MSTest-V2-Adapter nicht unterstützt. + + Test Run deployment issue: The assembly or module '{0}' was not found. Reason: {1} Problem bei der Testlaufbereitstellung: Die Assembly oder das Modul "{0}" wurde nicht gefunden. Ursache: {1} @@ -97,21 +289,309 @@ Problem bei der Testlaufbereitstellung: Die Assembly oder das Modul "{0}" wurde nicht gefunden. + + An older version of MSTestV2 package is loaded in assembly, test discovery might fail to discover all data tests if they depend on `.runsettings` file. + Eine ältere Version des MSTestV2-Pakets wird in die Assembly geladen. Bei der Testermittlung werden möglicherweise nicht alle Datentests ermittelt, wenn sie von der Datei ".runsettings" abhängen. + + + + Runsettings entry '<ExecutionApartmentState>STA</ExecutionApartmentState>' is not supported on non-Windows OSes. + Der Eintrag "<ExecutionApartmentState>STA</ExecutionApartmentState>" der Ausführungseinstellungen wird auf Nicht-Windows-Betriebssystemen nicht unterstützt. + + + + Running tests in any of the provided sources is not supported for the selected platform + Das Ausführen von Tests in einer der angegebenen Quellen wird für die ausgewählte Plattform nicht unterstützt. + + + + Failed to discover tests from assembly {0}. Reason:{1} + Fehler beim Ermitteln von Tests aus der Assembly "{0}". Ursache:{1} + + + + File does not exist: {0} + Die Datei ist nicht vorhanden: {0} + + + + Test cleanup method '{0}.{1}' timed out after {2}ms + Timeout der Testbereinigungsmethode "{0}.{1}" nach {2} ms + + + + Test cleanup method '{0}.{1}' was canceled + Die Testbereinigungsmethode "{0}.{1}" wurde abgebrochen + + + + TestContext cannot be Null. + "TestContext" darf nicht NULL sein. + + + + TestContext Messages: + TestContext-Meldungen: + + + + Test initialize method '{0}.{1}' timed out after {2}ms + Timeout der Testinitialisierungsmethode "{0}.{1}" nach {2} ms + + + + Test initialize method '{0}.{1}' was canceled + Die Testinitialisierungsmethode "{0}.{1}" wurde abgebrochen + + + + Test method {0} was not found. + Die Testmethode "{0}" wurde nicht gefunden. + + + + Test Parallelization enabled for {0} (Workers: {1}, Scope: {2}) + Testparallelisierung aktiviert für {0} (Workers: {1}, Scope: {2}) + {Locked="Workers"}{Locked="Scope"} + {0}_{1} {2} {0}{1} {2} + + Unable to load types from the test source '{0}'. Some or all of the tests in this source may not be discovered. +Error: {1} + Fehler beim Laden von Typen aus der Testquelle "{0}". Möglicherweise werden einige oder alle Tests in dieser Quelle nicht ermittelt. +Fehler: {1} + + + + Assembly Cleanup method {0}.{1} failed. Error Message: {2}. StackTrace: {3} + Fehler bei der Methode "{0}.{1}" für die Assemblybereinigung. Fehlermeldung: {2}. "StackTrace": {3} + + + + Assembly Initialization method {0}.{1} threw exception. {2}: {3}. Aborting test execution. + Die Methode "{0}.{1}" für die Assemblyinitialisierung hat eine Ausnahme ausgelöst. {2}: {3}. Die Ausführung des Tests wird abgebrochen. + + + + Class Cleanup method {0}.{1} failed. Error Message: {2}. Stack Trace: {3} + Fehler bei der Methode "{0}.{1}" für die Klassenbereinigung. Fehlermeldung: {2}. Stapelüberwachung: {3} + + + + Class Initialization method {0}.{1} threw exception. {2}: {3}. + Die Methode "{0}.{1}" für die Klasseninitialisierung hat eine Ausnahme ausgelöst. {2}: {3}. + + + + Method {0}.{1} has wrong signature. The method must be static, public, does not return a value and should not take any parameter. Additionally, if you are using async-await in method then return-type must be 'Task' or 'ValueTask'. + Die Methode „{0}.{1}“ weist eine falsche Signatur auf. Die Methode muss statisch und öffentlich sein. Sie darf keinen Wert zurückgeben und keinen Parameter annehmen. Wenn Sie außerdem in der Methode „async-await“ verwenden, muss der Rückgabetyp „Task“ oder „ValueTask“ sein. + + + + Method {0}.{1} has wrong signature. The method must be static, public, does not return a value and should take a single parameter of type TestContext. Additionally, if you are using async-await in method then return-type must be 'Task' or 'ValueTask'. + Die Methode „{0}.{1}“ weist eine falsche Signatur auf. Die Methode muss statisch und öffentlich sein. Sie darf keinen Wert zurückgeben und muss einen einzigen Parameter des Typs TestContext annehmen. Wenn Sie außerdem in der Methode „async-await“ verwenden, muss der Rückgabetyp „Task“ oder „ValueTask“ sein. + + + + TestCleanup method {0}.{1} threw exception. {2}. + Die TestCleanup-Methode "{0}.{1}" hat eine Ausnahme ausgelöst. {2}. + + + + Error calling Test Cleanup method for test class {0}: {1} + Fehler beim Aufruf der Testbereinigungsmethode für die Testklasse "{0}": {1} + + + + TestCleanup Stack Trace + TestCleanup-Stapelüberwachung + + Data source '{0}' cannot be found in the test configuration settings Die Datenquelle "{0}" wurde in den Testkonfigurationseinstellungen nicht gefunden. + + --- End of inner exception stack trace --- + --- Ende der internen Ausnahmestapelüberwachung --- + + The unit test adapter failed to connect to the data source or to read the data. For more information on troubleshooting this error, see "Troubleshooting Data-Driven Unit Tests" (http://go.microsoft.com/fwlink/?LinkId=62412) in the MSDN Library. Error details: {0} Der Komponententestadapter konnte keine Verbindung mit der Datenquelle herstellen bzw. die Daten nicht lesen. Weitere Informationen zur Problembehandlung dieses Fehlers finden Sie unter "Problembehandlung von datengesteuerten Komponententests" (http://go.microsoft.com/fwlink/?LinkId=62412) in der MSDN Library. Fehlerdetails: {0} + + UTA031: class {0} does not have valid TestContext property. TestContext must be of type TestContext, must be non-static, and must be public. For example: public TestContext TestContext. + UTA031: Die Klasse {0} weist keine gültige Eigenschaft TestContext auf. TestContext muss vom Typ TestContext sein, muss nicht statisch und öffentlich sein. Beispiel: 'public TestContext TestContext'. + + + + UTA007: Method {1} defined in class {0} does not have correct signature. Test method marked with the [TestMethod] attribute must be non-static, public, return-type as void and should not take any parameter. Example: public void Test.Class1.Test(). Additionally, if you are using async-await in test method then return-type must be 'Task' or 'ValueTask'. Example: public async Task Test.Class1.Test2() + UTA007: Die in der Klasse {0} definierte Methode {1} weist nicht die richtige Signatur auf. Die mit dem [TestMethod]-Attribut markierte Testmethode muss nicht statisch und öffentlich sein, muss den Rückgabetyp „void“ aufweisen und darf keine Parameter annehmen. Beispiel: public void Test.Class1.Test(). Wenn Sie außerdem in der Testmethode „async-await“ verwenden, muss der Rückgabetyp „Task“ oder „ValueTask“ sein. Beispiel: public async Task Test.Class1.Test2() + + + + UTA054: {0}.{1} has invalid Timeout attribute. The timeout must be an integer value greater than 0. + UTA054: {0}.{1} weist ein ungültiges Attribut „Timeout“ auf. Timeout muss eine ganze Zahl größer als 0 sein. + + + + UTA014: {0}: Cannot define more than one method with the AssemblyCleanup attribute inside an assembly. + UTA014: {0}: Es darf nur eine Methode mit dem Attribut 'AssemblyCleanup' innerhalb einer Assembly definiert werden. + + + + UTA013: {0}: Cannot define more than one method with the AssemblyInitialize attribute inside an assembly. + UTA013: {0}: Es darf nur eine Methode mit dem Attribut 'AssemblyInitialize' innerhalb einer Assembly definiert werden. + + + + UTA026: {0}: Cannot define more than one method with the ClassCleanup attribute inside a class. + UTA026: {0}: Es darf nur eine Methode mit dem Attribut 'ClassCleanup' innerhalb einer Klasse definiert werden. + + + + UTA025: {0}: Cannot define more than one method with the ClassInitialize attribute inside a class. + UTA025: {0}: Es darf nur eine Methode mit dem Attribut 'ClassInitialize' innerhalb einer Klasse definiert werden. + + + + UTA024: {0}: Cannot define more than one method with the TestCleanup attribute. + UTA024: {0}: Es darf nur eine Methode mit dem Attribut 'TestCleanup' definiert werden. + + + + UTA018: {0}: Cannot define more than one method with the TestInitialize attribute. + UTA018: {0}: Es darf nur eine Methode mit dem Attribut 'TestInitialize' definiert werden. + + + + UTA001: TestClass attribute defined on non-public class {0} + UTA001: Für die nicht öffentliche Klasse '{0}' definiertes Attribut 'TestClass'. + + + + UTA023: {0}: Cannot define predefined property {2} on method {1}. + UTA023: {0}: Die vordefinierte Eigenschaft "{2}" kann nicht für die Methode "{1}" definiert werden. + + + + TestClass attribute defined on generic non-abstract class {0} + TestClass-Attribut für generische nicht abstrakte Klasse {0} definiert + + + + UTA021: {0}: Null or empty custom property defined on method {1}. The custom property must have a valid name. + UTA021: {0}: Für die Methode "{1}" wurde eine benutzerdefinierte Eigenschaft mit dem Wert NULL oder eine benutzerdefinierte leere Eigenschaft definiert. Die benutzerdefinierte Eigenschaft muss einen gültigen Namen aufweisen. + + + + An unhandled exception was thrown by the 'Execute' method. Please report this error to the author of the attribute '{0}'. +{1} + Von der„Execute“-Methode wurde ein Ausnahmefehler ausgelöst. Melden Sie diesen Fehler dem Autor des Attributs „{0}“. +{1} + + + + The ExpectedException attribute defined on test method {0}.{1} threw an exception during construction. +{2} + Das für die Testmethode "{0}.{1}" definierte ExpectedException-Attribut hat während der Konstruktion eine Ausnahme ausgelöst. +{2} + + + + Failed to obtain the exception thrown by test method {0}.{1}. + Fehler beim Abrufen der von der Testmethode "{0}.{1}" ausgelösten Ausnahme. + + + + Initialization method {0}.{1} threw exception. {2}. + Die Initialisierungsmethode '{0}.{1}' hat eine Ausnahme ausgelöst. {2}. + + + + Unable to create instance of class {0}. Error: {1}. + Es kann keine Instanz der Klasse '{0}' erstellt werden. Fehler: {1}. + + + + Method {0}.{1} does not exist. + Die Methode "{0}.{1}" ist nicht vorhanden. + + + + The test method '{0}.{1}' has multiple attributes derived from '{2}' defined on it. Only one such attribute is allowed. + Für die Testmethode „{0}.{1}“ sind mehrere Attribute definiert, die von „{2}“ abgeleitet sind. Nur ein einziges solches Attribut ist zulässig. + + + + Error in executing test. No result returned by extension. If using extension of TestMethodAttribute then please contact vendor. + Fehler beim Ausführen des Tests. Von der Extension wurde kein Ergebnis zurückgegeben. Wenn Sie eine Extension von "TestMethodAttribute" verwenden, wenden Sie sich bitte an den Anbieter. + + + + Cannot find a valid constructor for test class '{0}'. Valid constructors are 'public' and either parameterless or with one parameter of type 'TestContext'. + Es wurde kein gültiger Konstruktor für die Testklasse "{0}" gefunden. Gültige Konstruktoren sind "public" und entweder parameterlos oder mit einem Parameter vom Typ "TestContext". + + + + Unable to find property {0}.TestContext. Error:{1}. + Die Eigenschaft "{0}.TestContext" wurde nicht gefunden. Fehler: {1}. + + + + Unable to set TestContext property for the class {0}. Error: {1}. + Die Eigenschaft 'TestContext' für die Klasse '{0}' kann nicht festgelegt werden. Fehler: {1}. + + + + The {0}.TestContext has incorrect type. + "{0}.TestContext" weist einen falschen Typ auf. + + + + Method {0}.{1} has wrong signature. The method must be non-static, public, does not return a value and should not take any parameter. Additionally, if you are using async-await in method then return-type must be 'Task' or 'ValueTask'. + Die Methode „{0}.{1}“ weist eine falsche Signatur auf. Die Methode muss nicht statisch und öffentlich sein, und sie darf keinen Wert zurückgeben und keinen Parameter annehmen. Wenn Sie außerdem in der Methode „async-await“ verwenden, muss der Rückgabetyp „Task“ oder „ValueTask“ sein. + + + + Test method {0}.{1} threw exception: +{2} + Die Testmethode "{0}.{1}" hat eine Ausnahme ausgelöst: +{2} + + + + Unable to get type {0}. Error: {1}. + Der Typ "{0}" kann nicht abgerufen werden. Fehler: {1}. + + + + The called code threw an exception that was caught, but the exception value was null + Der aufgerufene Code hat eine Ausnahme ausgelöst, die abgefangen wurde, aber der Ausnahmewert war NULL. + + + + {0} For UWP projects, if you are using UI objects in test consider using [UITestMethod] attribute instead of [TestMethod] to execute test in UI thread. + {0}: Erwägen Sie für UWP-Projekte bei Einsatz von UI-Objekten im Test die Verwendung des [UITestMethod]-Attributs anstelle von "[TestMethod]", um den Test im UI-Thread auszuführen. + + + + (Failed to get the message for an exception of type {0} due to an exception.) + (Fehler beim Abrufen der Meldung für eine Ausnahme vom Typ "{0}" aufgrund einer Ausnahme.) + + + + 'MSTest.TestAdapter' and 'MSTest.TestFramework' must have the same version. Found 'MSTest.TestAdapter' version '{0}' and 'MSTest.TestFramework' version '{1}'. Please make sure that the versions of 'MSTest.TestAdapter' and 'MSTest.TestFramework' NuGet packages have the same version. + „MSTest.TestAdapter“ und „MSTest.TestFramework“ müssen dieselbe Version aufweisen. Gefunden wurde „MSTest.TestAdapter“ Version „{0}“ und „MSTest.TestFramework“ Version „{1}“. Stellen Sie sicher, dass die Versionen der NuGet-Pakete „MSTest.TestAdapter“ und „MSTest.TestFramework“ übereinstimmen. + + Wrong number of objects for permutation. Should be greater than zero. Falsche Anzahl von Objekten für die Permutation. Die Anzahl muss größer als NULL sein. diff --git a/src/Adapter/MSTestAdapter.PlatformServices/Resources/xlf/Resource.es.xlf b/src/Adapter/MSTestAdapter.PlatformServices/Resources/xlf/Resource.es.xlf index 1c05e7b8c1..022571fcca 100644 --- a/src/Adapter/MSTestAdapter.PlatformServices/Resources/xlf/Resource.es.xlf +++ b/src/Adapter/MSTestAdapter.PlatformServices/Resources/xlf/Resource.es.xlf @@ -2,16 +2,116 @@ + + 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 + + + + Assembly cleanup method '{0}.{1}' was canceled + Método de limpieza de ensamblado "{0}.{1}" se canceló + + + + 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 + + + + Assembly initialize method '{0}.{1}' was canceled + Método de inicialización de ensamblado "{0}.{1}" se canceló + + + + MSTestAdapterV2 + MSTestAdapterV2 + + + + Exception occurred while enumerating IDataSource attribute on "{0}.{1}": {2} + Excepción al enumerar el atributo IDataSource en "{0}.{1}": {2} + {0}: TypeName with namespace, +{1}: Method name, +{2}: Exception details + + + Exception occurred while expanding IDataSource rows from attribute on "{0}.{1}": {2} + Se produjo una excepción al expandir las filas de IDataSource del atributo en "{0}.{1}": {2} + {0}: TypeName with namespace, +{1}: Method name, +{2}: CannotExpandIDataSourceAttribute_DuplicateDisplayName or CannotExpandIDataSourceAttribute_CannotSerialize + + + Data on index {0} for "{1}" cannot be serialized. All data provided through "IDataSource" should be serializable. If you need to test non-serializable data sources, please make sure you add "TestDataSourceDiscovery" attribute on your test assembly and set the discovery option to "DuringExecution". + No se pueden serializar los datos del {0} de índice para "{1}". Todos los datos proporcionados a través de "IDataSource" deben ser serializables. Si necesita probar orígenes de datos no serializables, asegúrese de agregar el atributo "TestDataSourceDiscovery" en el ensamblado de prueba y establezca la opción de detección en "DuringExecution". + {0}: Zero based index if an element inside of an array, +{1}: Test name + + + Display name "{2}" on indexes {0} and {1} are duplicate. Display names should be unique. + El nombre para mostrar "{2}" en los índices {0} y {1} están duplicados. Los nombres para mostrar deben ser únicos. + {0}, {1}: Zero based index if an element inside of an array +{2}: Test display name. + Could not find file '{0}'. No se pudo encontrar el archivo '{0}'. + + Cannot run test method '{0}.{1}': Test data doesn't match method parameters. Either the count or types are different. +Test expected {2} parameter(s), with types '{3}', +but received {4} argument(s), with types '{5}'. + No se puede ejecutar el método de prueba '{0}.{1}': Los datos de prueba no coinciden con los parámetros del método. El recuento o los tipos son diferentes. +Probar los parámetros de {2} esperados, con los tipos '{3}', +pero recibió {4} argumentos, con los tipos '{5}'. + + + + Cannot run test method '{0}.{1}': Method has parameters, but does not define any test source. Use '[DataRow]', '[DynamicData]', or a custom 'ITestDataSource' data source to provide test data. + No se puede ejecutar el método de prueba "{0}.{1}": el método tiene parámetros, pero no define ningún origen de prueba. Use "[DataRow]", "[DynamicData]" o un origen de datos "ITestDataSource" personalizado para proporcionar datos de prueba. + + + + 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 + + + + Class cleanup method '{0}.{1}' was canceled + Método de limpieza de clases "{0}.{1}" se canceló + + + + 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 + + + + Class initialize method '{0}.{1}' was canceled + Método de inicialización de clase "{0}.{1}" se canceló + + The parameter should not be null or empty. El parámetro no debe ser NULL ni estar vacío. + + MSTestAdapter failed to discover tests in class '{0}' of assembly '{1}' because {2}. + MSTestAdapter no detectó pruebas en la clase '{0}' del ensamblado '{1}' porque {2}. + + + + {0} (Data Row {1}) + {0} (Fila de datos {1}) + + + + Debug Trace: + Seguimiento de depuración: + + Test Run deployment issue: Bad deployment item: '{0}': output directory '{1}' specifies the item to be deployed outside deployment root directory which is not allowed. Problema de implementación en la serie de pruebas: elemento de implementación incorrecto '{0}': el directorio de salida '{1}' especifica el elemento que se va a implementar fuera del directorio raíz de implementación, lo cual no está permitido. @@ -77,16 +177,108 @@ elemento de implementación '{0}' (directorio de salida '{1}') + + [MSTest][Discovery][{0}] {1} + [MSTest][Discovery][{0}] {1} + + + + Both '.runsettings' and '.testconfig.json' files have been detected. Please select only one of these test configuration files. + Se han detectado los archivos ".runsettings" y ".testconfig.json". Seleccione solo uno de estos archivos de configuración de prueba. + + + + {0}: {1} + {0}: {1} + + + + "{0}": (Failed to get exception description due to an exception of type "{1}". + "{0}": (No se pudo obtener la descripción de la excepción debido a una excepción de tipo "{1}". + {0}: Type of the original exception that we're trying to get the description of. +{1}: Thrown exception + + + Exceptions thrown: + Excepciones devueltas: + This is usually precedes by TestAssembly_AssemblyDiscoveryFailure message, and preceded by list of exceptions thrown in a test discovery session. + + + Test '{0}' was canceled + Se canceló la prueba '{0}' + + + + Test '{0}' timed out after {1}ms + La prueba '{0}' agotó el tiempo de espera después de {1} ms + + + + Getting custom attributes for type {0} threw exception (will ignore and use the reflection way): {1} + Al obtener atributos personalizados para el tipo {0} se produjo una excepción (se omitirá y se usará la forma de reflexión): {1} + {0}: Attribute full type name. +{1}: Exception description + + + The type of the generic parameter '{0}' could not be inferred. + No se pudo inferir el tipo del parámetro genérico '{0}'. + + + + The generic test method '{0}' doesn't have arguments, so the generic parameter cannot be inferred. + El método de prueba genérico '{0}' no tiene argumentos, por lo que no se puede inferir el parámetro genérico. + + + + Found two conflicting types for generic parameter '{0}'. The conflicting types are '{1}' and '{2}'. + Se encontraron dos tipos en conflicto para el parámetro genérico '{0}'. Los tipos en conflicto son '{1}' y ''{2}. + + + + Invalid value '{0}' specified for 'ClassCleanupLifecycle'. Supported scopes are {1}. + Valor no válido "{0}" especificado para "ClassCleanupLifecycle". Los ámbitos admitidos son {1}. + {Locked="ClassCleanupLifecycle"} + + + Invalid value '{0}' specified for 'Scope'. Supported scopes are {1}. + Valor no válido "{0}" especificado para "Scope". Los ámbitos admitidos son {1}. + {Locked="Scope"} + + + Invalid value '{0}' specified for 'Workers'. The value should be a non-negative integer. + Valor no válido "{0}" especificado para "Workers". El valor debe ser un entero no negativo. + {Locked="Workers"} + + + Invalid settings '{0}'. Unexpected XmlAttribute: '{1}'. + Valor no válido '{0}'. XmlAttribute no esperado: '{1}'. + + MSTestAdapter encountered an unexpected element '{0}' in its settings '{1}'. Remove this element and try again. MSTestAdapter encontró un elemento inesperado '{0}' en su configuración '{1}'. Quite este elemento e inténtelo de nuevo. + + Invalid settings '{0}'. Unexpected XmlElement: '{1}'. + Valor no válido '{0}'. XmlElement no esperado: '{1}'. + + Invalid value '{0}' for runsettings entry '{1}', setting will be ignored. Valor ''{0}'' no válido para la entrada runsettings ''{1}'', se omitirá la configuración. + + Invalid value '{0}' for runsettings entry '{1}', setting will be ignored. + Valor ''{0}'' no válido para la entrada runsettings ''{1}'', se omitirá la configuración. + + + + Warning : A testsettings file or a vsmdi file is not supported with the MSTest V2 Adapter. + Advertencia: No se admite un archivo testsettings o vsmdi con el adaptador de MSTest V2. + + Test Run deployment issue: The assembly or module '{0}' was not found. Reason: {1} Problema de implementación en la serie de pruebas: no se encontró el ensamblado o el módulo '{0}'. Motivo: {1} @@ -97,21 +289,309 @@ Problema de implementación en la serie de pruebas: no se encontró el ensamblado o el módulo '{0}'. + + An older version of MSTestV2 package is loaded in assembly, test discovery might fail to discover all data tests if they depend on `.runsettings` file. + Hay una versión anterior del paquete MSTestV2 cargada en el ensamblado. Es posible que la detección de pruebas no detecte todas las pruebas de datos si dependen del archivo ".runsettings". + + + + Runsettings entry '<ExecutionApartmentState>STA</ExecutionApartmentState>' is not supported on non-Windows OSes. + La entrada runsettings "<ExecutionApartmentState>STA</ExecutionApartmentState>" no se admite en sistemas operativos que no son de Windows. + + + + Running tests in any of the provided sources is not supported for the selected platform + La ejecución de pruebas en las fuentes proporcionadas no se admite para la plataforma seleccionada + + + + Failed to discover tests from assembly {0}. Reason:{1} + No se pudieron detectar pruebas desde el ensamblado {0}. Motivo:{1} + + + + File does not exist: {0} + El archivo no existe: {0} + + + + 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 + + + + Test cleanup method '{0}.{1}' was canceled + Método de limpieza de pruebas "{0}.{1}" se canceló + + + + TestContext cannot be Null. + TestContext no será null. + + + + TestContext Messages: + Mensajes de TestContext: + + + + 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 + + + + Test initialize method '{0}.{1}' was canceled + Método de inicialización de prueba "{0}.{1}" se canceló + + + + Test method {0} was not found. + No encontrado método prueba {0}. + + + + Test Parallelization enabled for {0} (Workers: {1}, Scope: {2}) + Probar paralelización habilitada para {0} (Workers: {1}, Scope: {2}) + {Locked="Workers"}{Locked="Scope"} + {0}_{1} {2} {0} {1} {2} + + Unable to load types from the test source '{0}'. Some or all of the tests in this source may not be discovered. +Error: {1} + No se pueden cargar tipos del origen de prueba "{0}". Puede que no se detecten algunas o ninguna de las pruebas de este origen. +Error: {1} + + + + Assembly Cleanup method {0}.{1} failed. Error Message: {2}. StackTrace: {3} + Error de método Cleanup de ensamblado {0}.{1}. Mensaje de error: {2}. StackTrace: {3} + + + + Assembly Initialization method {0}.{1} threw exception. {2}: {3}. Aborting test execution. + Excepción método inicialización ensamblado {0}.{1}. {2}: {3}. Anulada ejecución de prueba. + + + + Class Cleanup method {0}.{1} failed. Error Message: {2}. Stack Trace: {3} + Error de método Cleanup de clase {0}.{1}. Mensaje error: {2}. Seguimiento de pila: {3} + + + + Class Initialization method {0}.{1} threw exception. {2}: {3}. + Excepción del método inicialización clase {0}. {1}. {2}: {3}. + + + + Method {0}.{1} has wrong signature. The method must be static, public, does not return a value and should not take any parameter. Additionally, if you are using async-await in method then return-type must be 'Task' or 'ValueTask'. + El método {0}.{1} tiene una firma incorrecta. El método debe ser estático, público, no devolver un valor y no aceptar ningún parámetro. Además, si está usando async-await en el método entonces el tipo de valor devuelto debe ser 'Task' o 'ValueTask'. + + + + Method {0}.{1} has wrong signature. The method must be static, public, does not return a value and should take a single parameter of type TestContext. Additionally, if you are using async-await in method then return-type must be 'Task' or 'ValueTask'. + El método {0}.{1} tiene una firma incorrecta. El método debe ser estático, público, no devolver ningún valor y debe tomar un único parámetro de tipo TestContext. Además, si está usando async-await en el método entonces el tipo de valor devuelto debe ser 'Task' o 'ValueTask'. + + + + TestCleanup method {0}.{1} threw exception. {2}. + El método TestCleanup {0}.{1} devolvió una excepción. {2}. + + + + Error calling Test Cleanup method for test class {0}: {1} + Error al llamar al método Test Cleanup para la clase de prueba {0}: {1} + + + + TestCleanup Stack Trace + Seguimiento de pila de TestCleanup + + Data source '{0}' cannot be found in the test configuration settings No se encuentra el origen de datos '{0}' en los valores de configuración de prueba + + --- End of inner exception stack trace --- + --- Fin del seguimiento de la pila de excepción interna --- + + The unit test adapter failed to connect to the data source or to read the data. For more information on troubleshooting this error, see "Troubleshooting Data-Driven Unit Tests" (http://go.microsoft.com/fwlink/?LinkId=62412) in the MSDN Library. Error details: {0} El adaptador de pruebas unitarias no puede conectarse al origen de datos o leer los datos. Para obtener más información acerca de cómo solucionar este error, consulte "Solución de problemas de pruebas unitarias orientadas a datos" (http://go.microsoft.com/fwlink/?LinkId=62412) en la biblioteca de MSDN. Detalles del error: {0} + + UTA031: class {0} does not have valid TestContext property. TestContext must be of type TestContext, must be non-static, and must be public. For example: public TestContext TestContext. + UTA031: la clase {0}no tiene la propiedad TestContext válida. TestContext debe ser de tipo TestContext, debe ser no estática y debe ser pública. Por ejemplo: public TestContext TestContext. + + + + UTA007: Method {1} defined in class {0} does not have correct signature. Test method marked with the [TestMethod] attribute must be non-static, public, return-type as void and should not take any parameter. Example: public void Test.Class1.Test(). Additionally, if you are using async-await in test method then return-type must be 'Task' or 'ValueTask'. Example: public async Task Test.Class1.Test2() + UTA007: El método {1} definido en la clase {0} no tiene la firma correcta. El método de prueba marcado con el atributo [TestMethod] debe ser no estático, público, con el tipo devuelto void y no debe tomar ningún parámetro. Ejemplo: public void Test.Class1.Test(). Además, si está usando async-await en el método de prueba, entonces el tipo de valor devuelto debe ser 'Task' o 'ValueTask'. Ejemplo: public async Task Test.Class1.Test2() + + + + UTA054: {0}.{1} has invalid Timeout attribute. The timeout must be an integer value greater than 0. + UTA054: {0}.{1} tiene un atributo Timeout no válido. El tiempo de espera debe ser un valor entero mayor que 0. + + + + UTA014: {0}: Cannot define more than one method with the AssemblyCleanup attribute inside an assembly. + UTA014: {0}: No se puede definir más de un método con el atributo AssemblyCleanup dentro de un ensamblado. + + + + UTA013: {0}: Cannot define more than one method with the AssemblyInitialize attribute inside an assembly. + UTA013: {0}: No se puede definir más de un método con el atributo AssemblyInitialize dentro de un ensamblado. + + + + UTA026: {0}: Cannot define more than one method with the ClassCleanup attribute inside a class. + UTA026: {0}: No se puede definir más de un método con el atributo ClassCleanup dentro de una clase. + + + + UTA025: {0}: Cannot define more than one method with the ClassInitialize attribute inside a class. + UTA025: {0}: No se puede definir más de un método con el atributo ClassInitialize dentro de una clase. + + + + UTA024: {0}: Cannot define more than one method with the TestCleanup attribute. + UTA024: {0}: No se puede definir más de un método con el atributo TestCleanup. + + + + UTA018: {0}: Cannot define more than one method with the TestInitialize attribute. + UTA018: {0}: No se puede definir más de un método con el atributo TestInitialize. + + + + UTA001: TestClass attribute defined on non-public class {0} + UTA001: se ha definido el atributo TestClass en la clase no pública {0} + + + + UTA023: {0}: Cannot define predefined property {2} on method {1}. + UTA023: {0}: no se puede definir la propiedad predefinida {2} en el método {1}. + + + + TestClass attribute defined on generic non-abstract class {0} + Atributo TestClass definido en una clase genérica no abstracta {0} + + + + UTA021: {0}: Null or empty custom property defined on method {1}. The custom property must have a valid name. + UTA021: {0}: se ha definido una propiedad personalizada nula o vacía en el método {1}. La propiedad personalizada debe tener un nombre válido. + + + + An unhandled exception was thrown by the 'Execute' method. Please report this error to the author of the attribute '{0}'. +{1} + El método 'Execute' produjo una excepción no controlada. Notifique este error al autor del atributo ''{0}. +{1} + + + + The ExpectedException attribute defined on test method {0}.{1} threw an exception during construction. +{2} + El atributo ExpectedException definido en el método de prueba {0}.{1} inició una excepción durante la construcción. +{2} + + + + Failed to obtain the exception thrown by test method {0}.{1}. + No se pudo obtener la excepción iniciada por el método de prueba {0}.{1}. + + + + Initialization method {0}.{1} threw exception. {2}. + El método de inicialización {0}.{1} devolvió una excepción. {2}. + + + + Unable to create instance of class {0}. Error: {1}. + No se puede crear una instancia de la clase {0}. Error: {1}. + + + + Method {0}.{1} does not exist. + El método {0}.{1} no existe. + + + + The test method '{0}.{1}' has multiple attributes derived from '{2}' defined on it. Only one such attribute is allowed. + El método de prueba '{0}.{1}' tiene varios atributos derivados de '{2}' definidos en él. Solo se permite un atributo de este tipo. + + + + Error in executing test. No result returned by extension. If using extension of TestMethodAttribute then please contact vendor. + No se pudo ejecutar prueba. Extensión no devolvió resultados. Si usa extensión TestMethodAttribute, contacte con el proveedor. + + + + Cannot find a valid constructor for test class '{0}'. Valid constructors are 'public' and either parameterless or with one parameter of type 'TestContext'. + No se encuentra un constructor válido para la clase de prueba '{0}'. Los constructores válidos son 'public' y sin parámetros o con un parámetro de tipo 'TestContext'. + + + + Unable to find property {0}.TestContext. Error:{1}. + No se puede encontrar la propiedad {0}.TestContext. Error:{1}. + + + + Unable to set TestContext property for the class {0}. Error: {1}. + No se puede establecer la propiedad TestContext para la clase {0}. Error: {1}. + + + + The {0}.TestContext has incorrect type. + Tipo {0}.TestContext no es correcto. + + + + Method {0}.{1} has wrong signature. The method must be non-static, public, does not return a value and should not take any parameter. Additionally, if you are using async-await in method then return-type must be 'Task' or 'ValueTask'. + El método {0}.{1} tiene una firma incorrecta. Debe ser un método no estático, público, no devolver ningún valor y no debe aceptar parámetros. Además, si está usando async-await en el método entonces el tipo de valor devuelto debe ser 'Task' o 'ValueTask'. + + + + Test method {0}.{1} threw exception: +{2} + Excepción método de prueba {0}.{1}: +{2} + + + + Unable to get type {0}. Error: {1}. + No se puede obtener el tipo {0}. Error: {1}. + + + + The called code threw an exception that was caught, but the exception value was null + El código llamado produjo una excepción que se detectó, pero el valor de la excepción era null + + + + {0} For UWP projects, if you are using UI objects in test consider using [UITestMethod] attribute instead of [TestMethod] to execute test in UI thread. + {0}. En proyectos de UWP, si usa objetos de interfaz de usuario en la prueba, podría usar el atributo [UITestMethod] en lugar de [TestMethod] para ejecutar la prueba en el subproceso de interfaz de usuario. + + + + (Failed to get the message for an exception of type {0} due to an exception.) + (No se pudo obtener el mensaje para una excepción del tipo {0} debido a una excepción.) + + + + 'MSTest.TestAdapter' and 'MSTest.TestFramework' must have the same version. Found 'MSTest.TestAdapter' version '{0}' and 'MSTest.TestFramework' version '{1}'. Please make sure that the versions of 'MSTest.TestAdapter' and 'MSTest.TestFramework' NuGet packages have the same version. + "MSTest.TestAdapter" y "MSTest.TestFramework" deben tener la misma versión. Se encontró la versión "{0}" de "MSTest.TestAdapter" y la versión "{1}" de "MSTest.TestFramework". Asegúrese de que las versiones de los paquetes NuGet "MSTest.TestAdapter" y "MSTest.TestFramework" tienen la misma versión. + + Wrong number of objects for permutation. Should be greater than zero. Número incorrecto de objetos para permutación. Debe ser una cantidad mayor que cero. diff --git a/src/Adapter/MSTestAdapter.PlatformServices/Resources/xlf/Resource.fr.xlf b/src/Adapter/MSTestAdapter.PlatformServices/Resources/xlf/Resource.fr.xlf index 0233a28fb8..fe1c9768e3 100644 --- a/src/Adapter/MSTestAdapter.PlatformServices/Resources/xlf/Resource.fr.xlf +++ b/src/Adapter/MSTestAdapter.PlatformServices/Resources/xlf/Resource.fr.xlf @@ -2,16 +2,116 @@ + + 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 + + + + Assembly cleanup method '{0}.{1}' was canceled + La méthode de nettoyage de l’assembly « {0}.{1} » a été annulée + + + + 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 + + + + Assembly initialize method '{0}.{1}' was canceled + La méthode d’initialisation de l’assembly « {0}.{1} » a été annulée + + + + MSTestAdapterV2 + MSTestAdapterV2 + + + + Exception occurred while enumerating IDataSource attribute on "{0}.{1}": {2} + Une exception s’est produite lors de l’énumération de l’attribut IDataSource sur « {0}.{1} » : {2} + {0}: TypeName with namespace, +{1}: Method name, +{2}: Exception details + + + Exception occurred while expanding IDataSource rows from attribute on "{0}.{1}": {2} + Une exception s’est produite lors du développement des lignes IDataSource à partir de l’attribut sur « {0}.{1} » : {2} + {0}: TypeName with namespace, +{1}: Method name, +{2}: CannotExpandIDataSourceAttribute_DuplicateDisplayName or CannotExpandIDataSourceAttribute_CannotSerialize + + + Data on index {0} for "{1}" cannot be serialized. All data provided through "IDataSource" should be serializable. If you need to test non-serializable data sources, please make sure you add "TestDataSourceDiscovery" attribute on your test assembly and set the discovery option to "DuringExecution". + Impossible de sérialiser les données de l’index {0} pour « {1} ». Toutes les données fournies via « IDataSource » doivent être sérialisables. Si vous devez tester des sources de données non sérialisables, veillez à ajouter l’attribut « TestDataSourceDiscovery » à votre assembly de test et définissez l’option de découverte sur « DuringExecution ». + {0}: Zero based index if an element inside of an array, +{1}: Test name + + + Display name "{2}" on indexes {0} and {1} are duplicate. Display names should be unique. + Le nom d’affichage « {2} » sur les index {0} et {1} est dupliqué. Les noms d’affichage doivent être uniques. + {0}, {1}: Zero based index if an element inside of an array +{2}: Test display name. + Could not find file '{0}'. Le fichier '{0}' est introuvable. + + Cannot run test method '{0}.{1}': Test data doesn't match method parameters. Either the count or types are different. +Test expected {2} parameter(s), with types '{3}', +but received {4} argument(s), with types '{5}'. + Impossible d’exécuter la méthode de test « {0}.{1} » : les données de test ne correspondent pas aux paramètres de la méthode. Le nombre ou les types sont différents. +Tester le ou les paramètres de {2} attendus, avec les types « {3} », +mais a reçu {4} argument(s), avec les types « {5} ». + + + + Cannot run test method '{0}.{1}': Method has parameters, but does not define any test source. Use '[DataRow]', '[DynamicData]', or a custom 'ITestDataSource' data source to provide test data. + Impossible d’exécuter la méthode de test « {0}.{1} » : la méthode a des paramètres, mais ne définit aucune source de test. Utilisez « [DataRow] », « [DynamicData] » ou une source de données « ITestDataSource » personnalisée pour fournir des données de test. + + + + 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 + + + + Class cleanup method '{0}.{1}' was canceled + La méthode de nettoyage de la classe « {0}.{1} » a été annulée + + + + 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 + + + + Class initialize method '{0}.{1}' was canceled + La méthode d’initialisation de la classe « {0}.{1} » a été annulée + + The parameter should not be null or empty. Le paramètre ne doit pas être une valeur Null ou être vide. + + MSTestAdapter failed to discover tests in class '{0}' of assembly '{1}' because {2}. + MSTestAdapter n'a pas découvert de tests dans la classe '{0}' de l'assembly '{1}', car {2}. + + + + {0} (Data Row {1}) + {0} (ligne de données {1}) + + + + Debug Trace: + Trace du débogage : + + Test Run deployment issue: Bad deployment item: '{0}': output directory '{1}' specifies the item to be deployed outside deployment root directory which is not allowed. Problème de déploiement de la série de tests : élément de déploiement incorrect : '{0}' : le répertoire de sortie '{1}' spécifie l'élément à déployer en dehors du répertoire racine du déploiement, ce qui n'est pas autorisé. @@ -77,16 +177,108 @@ élément de déploiement '{0}' (répertoire de sortie '{1}') + + [MSTest][Discovery][{0}] {1} + [MSTest][Discovery][{0}] {1} + + + + Both '.runsettings' and '.testconfig.json' files have been detected. Please select only one of these test configuration files. + Les fichiers « .runsettings » et « .testconfig.json » ont été détectés. Veuillez sélectionner un seul de ces fichiers de configuration de test. + + + + {0}: {1} + {0} : {1} + + + + "{0}": (Failed to get exception description due to an exception of type "{1}". + « {0} » : (Échec de l’obtention de la description de l’exception en raison d’une exception de type « {1} ». + {0}: Type of the original exception that we're trying to get the description of. +{1}: Thrown exception + + + Exceptions thrown: + Exceptions levées/s : + This is usually precedes by TestAssembly_AssemblyDiscoveryFailure message, and preceded by list of exceptions thrown in a test discovery session. + + + Test '{0}' was canceled + Le test {0} a été annulé + + + + Test '{0}' timed out after {1}ms + Le test « {0} » a expiré après {1} ms + + + + Getting custom attributes for type {0} threw exception (will ignore and use the reflection way): {1} + L’obtention d’attributs personnalisés pour le type {0} a levé une exception (ignorera et utilisera la méthode de réflexion) : {1} + {0}: Attribute full type name. +{1}: Exception description + + + The type of the generic parameter '{0}' could not be inferred. + Le type du paramètre générique « {0} » n'a pas pu être déduit. + + + + The generic test method '{0}' doesn't have arguments, so the generic parameter cannot be inferred. + La méthode de test générique « {0} » n’a pas d’arguments, donc le paramètre générique ne peut pas être déduit. + + + + Found two conflicting types for generic parameter '{0}'. The conflicting types are '{1}' and '{2}'. + Deux types en conflit ont été trouvés pour le paramètre générique « {0} ». Les types en conflit sont « {1} » et « {2} ». + + + + Invalid value '{0}' specified for 'ClassCleanupLifecycle'. Supported scopes are {1}. + Valeur non valide '{0}' spécifiée pour la 'ClassCleanupLifecycle'. Les portées prises en charge sont {1}. + {Locked="ClassCleanupLifecycle"} + + + Invalid value '{0}' specified for 'Scope'. Supported scopes are {1}. + Valeur non valide « {0} » spécifiée pour « Scope ». Les étendues prises en charge sont {1}. + {Locked="Scope"} + + + Invalid value '{0}' specified for 'Workers'. The value should be a non-negative integer. + Valeur non valide '{0}' spécifiée pour 'Workers'. La valeur doit être un entier non négatif. + {Locked="Workers"} + + + Invalid settings '{0}'. Unexpected XmlAttribute: '{1}'. + Paramètres non valides '{0}'. XmlAttribute inattendu : '{1}'. + + MSTestAdapter encountered an unexpected element '{0}' in its settings '{1}'. Remove this element and try again. MSTestAdapter a rencontré un élément inattendu '{0}' dans ses paramètres '{1}'. Supprimez cet élément, puis réessayez. + + Invalid settings '{0}'. Unexpected XmlElement: '{1}'. + Paramètres non valides '{0}'. XmlElement inattendu : '{1}'. + + Invalid value '{0}' for runsettings entry '{1}', setting will be ignored. Valeur non valide '{0}' pour l’entrée runsettings '{1}', le paramètre sera ignoré. + + Invalid value '{0}' for runsettings entry '{1}', setting will be ignored. + Valeur non valide '{0}' pour l’entrée runsettings '{1}', le paramètre sera ignoré. + + + + Warning : A testsettings file or a vsmdi file is not supported with the MSTest V2 Adapter. + Avertissement : L'adaptateur MSTest V2 ne prend pas en charge les fichiers testsettings ou vsmdi. + + Test Run deployment issue: The assembly or module '{0}' was not found. Reason: {1} Problème de déploiement de la série de tests : l'assembly ou le module '{0}' est introuvable. Raison : {1} @@ -97,21 +289,309 @@ Problème de déploiement de la série de tests : l'assembly ou le module '{0}' est introuvable. + + An older version of MSTestV2 package is loaded in assembly, test discovery might fail to discover all data tests if they depend on `.runsettings` file. + Une version antérieure du package MSTestV2 est chargée dans l’assembly. La découverte de tests risque de ne pas découvrir tous les tests de données s’ils dépendent du fichier '.runsettings'. + + + + Runsettings entry '<ExecutionApartmentState>STA</ExecutionApartmentState>' is not supported on non-Windows OSes. + L’entrée Runsettings « <ExecutionApartmentState>STA</ExecutionApartmentState> » n’est pas prise en charge sur les systèmes d’exploitation non Windows. + + + + Running tests in any of the provided sources is not supported for the selected platform + L'exécution de tests dans l'une des sources fournies n'est pas prise en charge pour la plateforme sélectionnée + + + + Failed to discover tests from assembly {0}. Reason:{1} + Échec de la découverte de tests à partir de l'assembly {0}. Raison :{1} + + + + File does not exist: {0} + Fichier inexistant : {0} + + + + 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 + + + + Test cleanup method '{0}.{1}' was canceled + La méthode de nettoyage du test « {0}.{1} » a été annulée + + + + TestContext cannot be Null. + TestContext ne peut pas être null. + + + + TestContext Messages: + Messages TestContext : + + + + 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 + + + + Test initialize method '{0}.{1}' was canceled + La méthode d’initialisation du test « {0}.{1} » a été annulée + + + + Test method {0} was not found. + Méthode de test {0} introuvable. + + + + Test Parallelization enabled for {0} (Workers: {1}, Scope: {2}) + Parallélisation des tests activée pour {0} (Workers : {1}, Scope : {2}) + {Locked="Workers"}{Locked="Scope"} + {0}_{1} {2} {0}_{1} {2} + + Unable to load types from the test source '{0}'. Some or all of the tests in this source may not be discovered. +Error: {1} + Impossible de charger les types à partir de la source de tests '{0}'. Une partie ou l'ensemble des tests de cette source ne peuvent pas être découverts. +Erreur : {1} + + + + Assembly Cleanup method {0}.{1} failed. Error Message: {2}. StackTrace: {3} + La méthode Cleanup d'assembly {0}.{1} a échoué. Message d'erreur : {2}. StackTrace : {3} + + + + Assembly Initialization method {0}.{1} threw exception. {2}: {3}. Aborting test execution. + La méthode d'assembly Initialization {0}.{1} a levé une exception. {2} : {3}. Abandon de l'exécution de tests. + + + + Class Cleanup method {0}.{1} failed. Error Message: {2}. Stack Trace: {3} + La méthode de classe Cleanup {0}.{1} a échoué. Message d'erreur : {2}. Trace de la pile : {3} + + + + Class Initialization method {0}.{1} threw exception. {2}: {3}. + La méthode de classe Initialization {0}.{1} a levé une exception. {2} : {3}. + + + + Method {0}.{1} has wrong signature. The method must be static, public, does not return a value and should not take any parameter. Additionally, if you are using async-await in method then return-type must be 'Task' or 'ValueTask'. + La méthode {0}.{1} présente une signature incorrecte. La méthode doit être statique, publique et ne doit retourner aucune valeur ni accepter aucun paramètre. En outre, si vous utilisez async-await dans la méthode, return-type doit être « Task » ou « ValueTask ». + + + + Method {0}.{1} has wrong signature. The method must be static, public, does not return a value and should take a single parameter of type TestContext. Additionally, if you are using async-await in method then return-type must be 'Task' or 'ValueTask'. + La méthode {0}.{1} présente une signature incorrecte. La méthode doit être statique, publique et ne doit retourner aucune valeur et accepter un seul paramètre de type TestContext. En outre, si vous utilisez async-await dans la méthode, return-type doit être « Task » ou « ValueTask ». + + + + TestCleanup method {0}.{1} threw exception. {2}. + La méthode TestCleanup {0}.{1} a levé une exception. {2}. + + + + Error calling Test Cleanup method for test class {0}: {1} + Erreur lors de l'appel de la méthode Test Cleanup pour la classe de test {0} : {1} + + + + TestCleanup Stack Trace + Trace de la pile TestCleanup + + Data source '{0}' cannot be found in the test configuration settings Source de données '{0}' introuvable dans les paramètres de configuration de test + + --- End of inner exception stack trace --- + --- Fin de la trace de la pile d'exception interne --- + + The unit test adapter failed to connect to the data source or to read the data. For more information on troubleshooting this error, see "Troubleshooting Data-Driven Unit Tests" (http://go.microsoft.com/fwlink/?LinkId=62412) in the MSDN Library. Error details: {0} L'adaptateur de test unitaire n'a pas réussi à se connecter à la source de données ou à lire les données. Pour plus d'informations sur le dépannage de l'erreur, consultez "Résolution des problèmes liés aux tests unitaires pilotés par les données" (http://go.microsoft.com/fwlink/?LinkId=62412) dans MSDN Library. Détails de l'erreur : {0} + + UTA031: class {0} does not have valid TestContext property. TestContext must be of type TestContext, must be non-static, and must be public. For example: public TestContext TestContext. + UTA031 : la classe {0} n'a pas de propriété TestContext valide. TestContext doit être de type TestContext, doit être non statique et doit être public. Par exemple : public TestContext TestContext. + + + + UTA007: Method {1} defined in class {0} does not have correct signature. Test method marked with the [TestMethod] attribute must be non-static, public, return-type as void and should not take any parameter. Example: public void Test.Class1.Test(). Additionally, if you are using async-await in test method then return-type must be 'Task' or 'ValueTask'. Example: public async Task Test.Class1.Test2() + UTA007 : la méthode {1} définie dans la classe {0} ne dispose pas d'une signature correcte. Une méthode de test marquée avec l'attribut [TestMethod] doit être non statique, doit utiliser void pour return-type et ne doit accepter aucun paramètre. Exemple : public void Test.Class1.Test(). En outre, si vous utilisez async-await dans la méthode test, return-type doit être « Task » ou « ValueTask ». Exemple : public async Task Test.Class1.Test2() + + + + UTA054: {0}.{1} has invalid Timeout attribute. The timeout must be an integer value greater than 0. + UTA054 : {0}.{1}possède un attribut de délai d’expiration non valide. Le délai d’expiration doit être un nombre entier supérieur à 0. + + + + UTA014: {0}: Cannot define more than one method with the AssemblyCleanup attribute inside an assembly. + UTA014 : {0} : impossible de définir plus d'une méthode avec l'attribut AssemblyCleanup à l'intérieur d'un assembly. + + + + UTA013: {0}: Cannot define more than one method with the AssemblyInitialize attribute inside an assembly. + UTA013 : {0} : impossible de définir plus d'une méthode avec l'attribut AssemblyInitialize à l'intérieur d'un assembly. + + + + UTA026: {0}: Cannot define more than one method with the ClassCleanup attribute inside a class. + UTA026 : {0} : impossible de définir plus d'une méthode avec l'attribut ClassCleanup à l'intérieur d'une classe. + + + + UTA025: {0}: Cannot define more than one method with the ClassInitialize attribute inside a class. + UTA025 : {0} : impossible de définir plus d'une méthode avec l'attribut ClassInitialize à l'intérieur d'une classe. + + + + UTA024: {0}: Cannot define more than one method with the TestCleanup attribute. + UTA024 : {0} : impossible de définir plus d'une méthode avec l'attribut TestCleanup. + + + + UTA018: {0}: Cannot define more than one method with the TestInitialize attribute. + UTA018 : {0} : impossible de définir plus d'une méthode avec l'attribut TestInitialize. + + + + UTA001: TestClass attribute defined on non-public class {0} + UTA001 : attribut TestClass défini sur la classe non publique {0} + + + + UTA023: {0}: Cannot define predefined property {2} on method {1}. + UTA023 : {0} : Impossible de définir la propriété prédéfinie {2} sur la méthode {1}. + + + + TestClass attribute defined on generic non-abstract class {0} + Attribut TestClass défini sur une classe non abstraite générique {0} + + + + UTA021: {0}: Null or empty custom property defined on method {1}. The custom property must have a valid name. + UTA021 : {0} : Une propriété null ou vide personnalisée est définie sur la méthode {1}. La propriété personnalisée doit posséder un nom valide. + + + + An unhandled exception was thrown by the 'Execute' method. Please report this error to the author of the attribute '{0}'. +{1} + Une exception non gérée a été levée par la méthode « Execute ». Veuillez signaler cette erreur à l’auteur de l’attribut « {0} ». +{1} + + + + The ExpectedException attribute defined on test method {0}.{1} threw an exception during construction. +{2} + L'attribut ExpectedException défini dans la méthode de test {0}.{1} a levé une exception durant la construction. +{2} + + + + Failed to obtain the exception thrown by test method {0}.{1}. + Échec de l'obtention de l'exception levée par la méthode de test {0}.{1}. + + + + Initialization method {0}.{1} threw exception. {2}. + La méthode Initialization {0}.{1} a levé une exception. {2}. + + + + Unable to create instance of class {0}. Error: {1}. + Impossible de créer une instance de la classe {0}. Erreur : {1}. + + + + Method {0}.{1} does not exist. + La méthode {0}.{1} n'existe pas. + + + + The test method '{0}.{1}' has multiple attributes derived from '{2}' defined on it. Only one such attribute is allowed. + La méthode de test « {0}.{1} » possède plusieurs attributs dérivés de « {2} » qui lui sont définis. Un seul attribut de ce type est autorisé. + + + + Error in executing test. No result returned by extension. If using extension of TestMethodAttribute then please contact vendor. + Erreur lors de l'exécution du test. L'extension n'a retourné aucun résultat. Si vous utilisez l'extension de TestMethodAttribute, contactez le fournisseur. + + + + Cannot find a valid constructor for test class '{0}'. Valid constructors are 'public' and either parameterless or with one parameter of type 'TestContext'. + Impossible de trouver un constructeur valide pour la classe de test « {0} ». Les constructeurs valides sont « publics » et sans paramètre ou avec un paramètre de type « TestContext ». + + + + Unable to find property {0}.TestContext. Error:{1}. + Propriété {0}.TestContext introuvable. Erreur :{1}. + + + + Unable to set TestContext property for the class {0}. Error: {1}. + Impossible de définir la propriété TestContext pour la classe {0}. Erreur : {1}. + + + + The {0}.TestContext has incorrect type. + {0}.TestContext possède un type incorrect. + + + + Method {0}.{1} has wrong signature. The method must be non-static, public, does not return a value and should not take any parameter. Additionally, if you are using async-await in method then return-type must be 'Task' or 'ValueTask'. + La méthode {0}.{1} présente une signature incorrecte. La méthode doit être non statique, publique et ne doit retourner aucune valeur ni accepter aucun paramètre. En outre, si vous utilisez async-await dans la méthode, return-type doit être « Task » ou « ValueTask ». + + + + Test method {0}.{1} threw exception: +{2} + La méthode de test {0}.{1} a levé une exception : +{2} + + + + Unable to get type {0}. Error: {1}. + Impossible d'obtenir le type {0}. Erreur : {1}. + + + + The called code threw an exception that was caught, but the exception value was null + Le code appelé a levé une exception qui a été interceptée, mais la valeur de l’exception était nul + + + + {0} For UWP projects, if you are using UI objects in test consider using [UITestMethod] attribute instead of [TestMethod] to execute test in UI thread. + {0} Pour les projets UWP, si vous utilisez des objets d'IU dans un test, utilisez l'attribut [UITestMethod] à la place de [TestMethod] pour exécuter le test dans le thread d'interface utilisateur. + + + + (Failed to get the message for an exception of type {0} due to an exception.) + (Échec de la réception du message pour une exception de type {0} en raison d'une exception.) + + + + 'MSTest.TestAdapter' and 'MSTest.TestFramework' must have the same version. Found 'MSTest.TestAdapter' version '{0}' and 'MSTest.TestFramework' version '{1}'. Please make sure that the versions of 'MSTest.TestAdapter' and 'MSTest.TestFramework' NuGet packages have the same version. + « MSTest.TestAdapter » et « MSTest.TestFramework » doivent avoir la même version. Version de « MSTest.TestAdapter » : « {0} » et version de « MSTest.TestFramework » : « {1} ». Veuillez vous assurer que les versions des packages NuGet « MSTest.TestAdapter » et « MSTest.TestFramework » sont identiques. + + Wrong number of objects for permutation. Should be greater than zero. Nombre d'objets erroné pour la permutation. Il doit être supérieur à zéro. diff --git a/src/Adapter/MSTestAdapter.PlatformServices/Resources/xlf/Resource.it.xlf b/src/Adapter/MSTestAdapter.PlatformServices/Resources/xlf/Resource.it.xlf index 4808a47f8f..01041f4d35 100644 --- a/src/Adapter/MSTestAdapter.PlatformServices/Resources/xlf/Resource.it.xlf +++ b/src/Adapter/MSTestAdapter.PlatformServices/Resources/xlf/Resource.it.xlf @@ -2,16 +2,116 @@ + + Assembly cleanup method '{0}.{1}' timed out after {2}ms + Metodo di pulizia dell’assembly '{0}. Time out di {1}' dopo {2} ms + + + + Assembly cleanup method '{0}.{1}' was canceled + Il metodo di pulizia dell'assembly "{0}.{1}" è stato annullato + + + + Assembly initialize method '{0}.{1}' timed out after {2}ms + Metodo di inizializzazione dell'assembly '{0}. Timeout di {1}' dopo {2} ms + + + + Assembly initialize method '{0}.{1}' was canceled + Il metodo di inizializzazione dell'assembly "{0}.{1}" è stato annullato + + + + MSTestAdapterV2 + MSTestAdapterV2 + + + + Exception occurred while enumerating IDataSource attribute on "{0}.{1}": {2} + Si è verificata un'eccezione durante l'enumerazione dell'attributo IDataSource in "{0}.{1}": {2} + {0}: TypeName with namespace, +{1}: Method name, +{2}: Exception details + + + Exception occurred while expanding IDataSource rows from attribute on "{0}.{1}": {2} + Si è verificata un'eccezione durante l'espansione delle righe IDataSource dall'attributo in "{0}.{1}": {2} + {0}: TypeName with namespace, +{1}: Method name, +{2}: CannotExpandIDataSourceAttribute_DuplicateDisplayName or CannotExpandIDataSourceAttribute_CannotSerialize + + + Data on index {0} for "{1}" cannot be serialized. All data provided through "IDataSource" should be serializable. If you need to test non-serializable data sources, please make sure you add "TestDataSourceDiscovery" attribute on your test assembly and set the discovery option to "DuringExecution". + Non è possibile serializzare i dati nell'indice {0} per "{1}". Tutti i dati forniti tramite "IDataSource" devono essere serializzabili. Se è necessario testare origini dati non serializzabili, assicurarsi di aggiungere l'attributo "TestDataSourceDiscovery" nell'assembly di test e impostare l'opzione di individuazione su "DuringExecution". + {0}: Zero based index if an element inside of an array, +{1}: Test name + + + Display name "{2}" on indexes {0} and {1} are duplicate. Display names should be unique. + Il nome visualizzato "{2}" negli indici {0} e {1} è duplicato. I nomi visualizzati devono essere univoci. + {0}, {1}: Zero based index if an element inside of an array +{2}: Test display name. + Could not find file '{0}'. Il file '{0}' non è stato trovato. + + Cannot run test method '{0}.{1}': Test data doesn't match method parameters. Either the count or types are different. +Test expected {2} parameter(s), with types '{3}', +but received {4} argument(s), with types '{5}'. + Non è possibile eseguire il metodo di test '{0}.{1}': i dati del test non corrispondono ai parametri del metodo. Il numero o il tipo è diverso. +Il test prevedeva {2} parametri, con tipi '{3}', +ma ha ricevuto {4} argomenti, con tipi '{5}'. + + + + Cannot run test method '{0}.{1}': Method has parameters, but does not define any test source. Use '[DataRow]', '[DynamicData]', or a custom 'ITestDataSource' data source to provide test data. + Impossibile eseguire il metodo di test "{0}.{1}": il metodo contiene parametri, ma non definisce alcuna origine test. Usare "[DataRow]", "[DynamicData]" o un'origine dati "ITestDataSource" personalizzata per fornire i dati del test. + + + + 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 + + + + Class cleanup method '{0}.{1}' was canceled + Il metodo di pulizia della classe "{0}.{1}" è stato annullato + + + + Class initialize method '{0}.{1}' timed out after {2}ms + Metodo di inizializzazione della classe '{0}. Timeout di {1}' dopo {2} ms + + + + Class initialize method '{0}.{1}' was canceled + Il metodo di inizializzazione della classe "{0}.{1}" è stato annullato + + The parameter should not be null or empty. Il parametro non deve essere vuoto o Null. + + MSTestAdapter failed to discover tests in class '{0}' of assembly '{1}' because {2}. + MSTestAdapter non è riuscito a individuare test nella classe '{0}' dell'assembly '{1}' perché {2}. + + + + {0} (Data Row {1}) + {0} (riga dati {1}) + + + + Debug Trace: + Traccia di debug: + + Test Run deployment issue: Bad deployment item: '{0}': output directory '{1}' specifies the item to be deployed outside deployment root directory which is not allowed. Problema di distribuzione dell'esecuzione dei test: l'elemento di distribuzione '{0}' non è corretto. La directory di output '{1}' specifica l'elemento da distribuire all'esterno della directory radice della distribuzione e questa operazione non consentita. @@ -77,16 +177,108 @@ elemento di distribuzione '{0}' (directory di output '{1}') + + [MSTest][Discovery][{0}] {1} + [MSTest][Individuazione][{0}] {1} + + + + Both '.runsettings' and '.testconfig.json' files have been detected. Please select only one of these test configuration files. + Sono stati rilevati sia i file '.runsettings' sia '.testconfig.json'. Selezionare solo uno di questi file di configurazione di test. + + + + {0}: {1} + {0}: {1} + + + + "{0}": (Failed to get exception description due to an exception of type "{1}". + "{0}": (non è stato possibile ottenere la descrizione dell'eccezione a causa di un'eccezione di tipo "{1}". + {0}: Type of the original exception that we're trying to get the description of. +{1}: Thrown exception + + + Exceptions thrown: + Eccezioni generate: + This is usually precedes by TestAssembly_AssemblyDiscoveryFailure message, and preceded by list of exceptions thrown in a test discovery session. + + + Test '{0}' was canceled + Il test '{0}' è stato annullato + + + + Test '{0}' timed out after {1}ms + Timeout del test '{0}' dopo {1} ms + + + + Getting custom attributes for type {0} threw exception (will ignore and use the reflection way): {1} + Il recupero degli attributi personalizzati per il tipo {0} ha generato un'eccezione (verrà ignorata e verrà usata la modalità reflection): {1} + {0}: Attribute full type name. +{1}: Exception description + + + The type of the generic parameter '{0}' could not be inferred. + Non è possibile dedurre il tipo del parametro generico '{0}'. + + + + The generic test method '{0}' doesn't have arguments, so the generic parameter cannot be inferred. + Il metodo di test generico '{0}' non ha argomenti, quindi non è possibile dedurre il parametro generico. + + + + Found two conflicting types for generic parameter '{0}'. The conflicting types are '{1}' and '{2}'. + Sono stati trovati due tipi in conflitto per il parametro generico '{0}'. I tipi in conflitto sono '{1}' e '{2}'. + + + + Invalid value '{0}' specified for 'ClassCleanupLifecycle'. Supported scopes are {1}. + Il valore '{0}', specificato per 'ClassCleanupLifecycle', non è valido. I valori supportati per Scope sono {1}. + {Locked="ClassCleanupLifecycle"} + + + Invalid value '{0}' specified for 'Scope'. Supported scopes are {1}. + Il valore '{0}', specificato per 'Scope', non è valido. I valori supportati per Scope sono {1}. + {Locked="Scope"} + + + Invalid value '{0}' specified for 'Workers'. The value should be a non-negative integer. + Il valore '{0}', specificato per 'Workers', non è valido. Il valore deve essere un numero intero non negativo. + {Locked="Workers"} + + + Invalid settings '{0}'. Unexpected XmlAttribute: '{1}'. + Le impostazioni '{0}' non sono valide. Elemento XmlAttribute imprevisto: '{1}'. + + MSTestAdapter encountered an unexpected element '{0}' in its settings '{1}'. Remove this element and try again. MSTestAdapter ha rilevato un elemento imprevisto '{0}' nelle impostazioni '{1}'. Rimuovere l'elemento e riprovare. + + Invalid settings '{0}'. Unexpected XmlElement: '{1}'. + Le impostazioni '{0}' non sono valide. Elemento XmlElement imprevisto: '{1}'. + + Invalid value '{0}' for runsettings entry '{1}', setting will be ignored. Valore non valido '{0}' per la voce runsettings '{1}'. L'impostazione verrà ignorata. + + Invalid value '{0}' for runsettings entry '{1}', setting will be ignored. + Valore non valido '{0}' per la voce runsettings '{1}'. L'impostazione verrà ignorata. + + + + Warning : A testsettings file or a vsmdi file is not supported with the MSTest V2 Adapter. + Avviso: con l'adattatore MSTest V2 non è possibile usare un file testsettings o un file vsmdi. + + Test Run deployment issue: The assembly or module '{0}' was not found. Reason: {1} Problema di distribuzione dell'esecuzione dei test: l'assembly o il modulo '{0}' non è stato trovato. Motivo: {1} @@ -97,21 +289,309 @@ Problema di distribuzione dell'esecuzione dei test: l'assembly o il modulo '{0}' non è stato trovato. + + An older version of MSTestV2 package is loaded in assembly, test discovery might fail to discover all data tests if they depend on `.runsettings` file. + Nell'assembly è caricata una versione precedente del pacchetto MSTestV2. L'individuazione dei test potrebbe non riuscire a individuare tutti i test dei dati se dipendono dal file '.runsettings'. + + + + Runsettings entry '<ExecutionApartmentState>STA</ExecutionApartmentState>' is not supported on non-Windows OSes. + La voce Runsettings '<ExecutionApartmentState>STA</ExecutionApartmentState>' non è supportata nei sistemi operativi non Windows. + + + + Running tests in any of the provided sources is not supported for the selected platform + L'esecuzione di test in una delle origini specificate non è supportata per la piattaforma selezionata + + + + Failed to discover tests from assembly {0}. Reason:{1} + Non è stato possibile individuare i test dall'assembly {0}. Motivo: {1} + + + + File does not exist: {0} + Il file {0} non esiste + + + + 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 + + + + Test cleanup method '{0}.{1}' was canceled + Il metodo di pulizia del test "{0}.{1}" è stato annullato + + + + TestContext cannot be Null. + TestContext non può essere Null. + + + + TestContext Messages: + Messaggi di TestContext: + + + + Test initialize method '{0}.{1}' timed out after {2}ms + Metodo di inizializzazione del test '{0}. Timeout di {1}' dopo {2} ms + + + + Test initialize method '{0}.{1}' was canceled + Il metodo di inizializzazione del test "{0}.{1}" è stato annullato + + + + Test method {0} was not found. + Il metodo di test {0} non è stato trovato. + + + + Test Parallelization enabled for {0} (Workers: {1}, Scope: {2}) + Parallelizzazione test abilitata per {0} (Workers: {1}. Scope: {2}) + {Locked="Workers"}{Locked="Scope"} + {0}_{1} {2} {0}_{1} {2} + + Unable to load types from the test source '{0}'. Some or all of the tests in this source may not be discovered. +Error: {1} + Non è possibile caricare i tipi dall'origine test '{0}'. È possibile che alcuni o tutti i test non siano stati individuati in questa origine. +Errore: {1} + + + + Assembly Cleanup method {0}.{1} failed. Error Message: {2}. StackTrace: {3} + Il metodo di pulizia assembly {0}.{1} non è riuscito. Messaggio di errore: {2}. Analisi dello stack: {3} + + + + Assembly Initialization method {0}.{1} threw exception. {2}: {3}. Aborting test execution. + Il metodo di inizializzazione assembly {0}.{1} ha generato un'eccezione. {2}: {3}. L'esecuzione del test verrà interrotta. + + + + Class Cleanup method {0}.{1} failed. Error Message: {2}. Stack Trace: {3} + Il metodo di pulizia classi {0}.{1} non è riuscito. Messaggio di errore: {2}. Analisi dello stack: {3} + + + + Class Initialization method {0}.{1} threw exception. {2}: {3}. + Il metodo di inizializzazione classi {0}.{1} ha generato un'eccezione. {2}: {3}. + + + + Method {0}.{1} has wrong signature. The method must be static, public, does not return a value and should not take any parameter. Additionally, if you are using async-await in method then return-type must be 'Task' or 'ValueTask'. + La firma del metodo {0}.{1}non è corretta. Il metodo deve essere statico e pubblico, non deve restituire un valore né accettare parametri. Se inoltre si usa async-await nel metodo di test, il tipo restituito deve essere 'Task' o 'ValueTask'. + + + + Method {0}.{1} has wrong signature. The method must be static, public, does not return a value and should take a single parameter of type TestContext. Additionally, if you are using async-await in method then return-type must be 'Task' or 'ValueTask'. + La firma del metodo {0}.{1}non è corretta. Il metodo deve essere statico e pubblico, non deve restituire un valore e deve accettare un singolo parametro di tipo TestContext. Se inoltre si usa async-await nel metodo di test, il tipo restituito deve essere 'Task' o 'ValueTask'. + + + + TestCleanup method {0}.{1} threw exception. {2}. + Il metodo TestCleanup {0}.{1} ha generato un'eccezione. {2}. + + + + Error calling Test Cleanup method for test class {0}: {1} + Si è verificato un errore durante la chiamata del metodo TestCleanup per la classe di test {0}: {1} + + + + TestCleanup Stack Trace + Analisi dello stato di TestCleanup + + Data source '{0}' cannot be found in the test configuration settings L'origine dati '{0}' non è stata trovata nelle impostazioni della configurazione di test + + --- End of inner exception stack trace --- + --- Fine dell'analisi dello stack dell'eccezione interna --- + + The unit test adapter failed to connect to the data source or to read the data. For more information on troubleshooting this error, see "Troubleshooting Data-Driven Unit Tests" (http://go.microsoft.com/fwlink/?LinkId=62412) in the MSDN Library. Error details: {0} L'adattatore di unit test non è riuscito a connettersi all'origine dati o a leggere i dati. Per altre informazioni sulla risoluzione di questo errore, vedere "Risoluzione dei problemi relativi a unit test basati su dati" (http://go.microsoft.com/fwlink/?LinkId=62412) in MSDN Library. Dettagli errore: {0} + + UTA031: class {0} does not have valid TestContext property. TestContext must be of type TestContext, must be non-static, and must be public. For example: public TestContext TestContext. + UTA031: la classe {0} non dispone di una proprietà TestContext valida. La proprietà TestContext deve essere di tipo TestContext, non deve essere statica e deve essere pubblica. Ad esempio: public TestContext TestContext. + + + + UTA007: Method {1} defined in class {0} does not have correct signature. Test method marked with the [TestMethod] attribute must be non-static, public, return-type as void and should not take any parameter. Example: public void Test.Class1.Test(). Additionally, if you are using async-await in test method then return-type must be 'Task' or 'ValueTask'. Example: public async Task Test.Class1.Test2() + UTA007: la firma del metodo {1} definito nella classe {0} non è corretta. Il metodo di test contrassegnato con l'attributo [TestMethod] deve essere pubblico e non statico, non deve accettare parametri e deve includere un tipo restituito void. Esempio: public void Test.Class1.Test(). Se inoltre nel metodo di test si usa async-await, il tipo restituito deve essere 'Task' o 'ValueTask'. Esempio: public async Task Test.Class1.Test2() + + + + UTA054: {0}.{1} has invalid Timeout attribute. The timeout must be an integer value greater than 0. + UTA054: {0}.{1} ha un attributo Timeout non valido. Il timeout deve essere un valore intero maggiore di 0. + + + + UTA014: {0}: Cannot define more than one method with the AssemblyCleanup attribute inside an assembly. + UTA014: {0}: non è possibile definire più di un metodo con l'attributo AssemblyCleanup all'interno di un assembly. + + + + UTA013: {0}: Cannot define more than one method with the AssemblyInitialize attribute inside an assembly. + UTA013: {0}: non è possibile definire più di un metodo con l'attributo AssemblyInitialize all'interno di un assembly. + + + + UTA026: {0}: Cannot define more than one method with the ClassCleanup attribute inside a class. + UTA026: {0}: non è possibile definire più di un metodo con l'attributo ClassCleanup all'interno di una classe. + + + + UTA025: {0}: Cannot define more than one method with the ClassInitialize attribute inside a class. + UTA025: {0}: non è possibile definire più di un metodo con l'attributo ClassInitialize all'interno di una classe. + + + + UTA024: {0}: Cannot define more than one method with the TestCleanup attribute. + UTA024: {0}: non è possibile definire più di un metodo con l'attributo TestCleanup. + + + + UTA018: {0}: Cannot define more than one method with the TestInitialize attribute. + UTA018: {0}: non è possibile definire più di un metodo con l'attributo TestInitialize. + + + + UTA001: TestClass attribute defined on non-public class {0} + UTA001: è stato definito l'attributo TestClass per la classe non pubblica {0} + + + + UTA023: {0}: Cannot define predefined property {2} on method {1}. + UTA023: {0}: non è possibile definire la proprietà predefinita {2} per il metodo {1}. + + + + TestClass attribute defined on generic non-abstract class {0} + Attributo TestClass definito nella classe generica non astratta {0} + + + + UTA021: {0}: Null or empty custom property defined on method {1}. The custom property must have a valid name. + UTA021: {0}: per il metodo {1} è stata definita una proprietà personalizzata Null o vuota. Specificare un nome valido per la proprietà personalizzata. + + + + An unhandled exception was thrown by the 'Execute' method. Please report this error to the author of the attribute '{0}'. +{1} + Un'eccezione non gestita è stata generata dal metodo 'Execute'. Segnalare questo errore all'autore dell'attributo '{0}'. +{1} + + + + The ExpectedException attribute defined on test method {0}.{1} threw an exception during construction. +{2} + L'attributo ExpectedException definito nel metodo di test {0}.{1} ha generato un'eccezione durante la costruzione. +{2} + + + + Failed to obtain the exception thrown by test method {0}.{1}. + Non è stato possibile ottenere l'eccezione generata dal metodo di test {0}.{1}. + + + + Initialization method {0}.{1} threw exception. {2}. + Il metodo di inizializzazione {0}.{1} ha generato un'eccezione. {2}. + + + + Unable to create instance of class {0}. Error: {1}. + Non è possibile creare un'istanza della classe {0}. Errore: {1}. + + + + Method {0}.{1} does not exist. + Il metodo {0}.{1} non esiste. + + + + The test method '{0}.{1}' has multiple attributes derived from '{2}' defined on it. Only one such attribute is allowed. + Il metodo di test '{0}.{1}' contiene più attributi derivati da '{2}' definito in esso. È consentito solo uno di tali attributi. + + + + Error in executing test. No result returned by extension. If using extension of TestMethodAttribute then please contact vendor. + Si è verificato un errore durante l'esecuzione del test. L'estensione non ha restituito alcun risultato. Se si usa l'estensione di TestMethodAttribute, contattare il fornitore. + + + + Cannot find a valid constructor for test class '{0}'. Valid constructors are 'public' and either parameterless or with one parameter of type 'TestContext'. + Impossibile trovare un costruttore valido per la classe di test '{0}'. I costruttori validi sono 'public' e senza parametri o con un parametro di tipo 'TestContext'. + + + + Unable to find property {0}.TestContext. Error:{1}. + La proprietà {0}.TestContext non è stata trovata. Errore: {1}. + + + + Unable to set TestContext property for the class {0}. Error: {1}. + Non è possibile impostare la proprietà TestContext per la classe {0}. Errore: {1}. + + + + The {0}.TestContext has incorrect type. + Il tipo di {0}.TestContext non è corretto. + + + + Method {0}.{1} has wrong signature. The method must be non-static, public, does not return a value and should not take any parameter. Additionally, if you are using async-await in method then return-type must be 'Task' or 'ValueTask'. + La firma del metodo {0}.{1}non è corretta. Il metodo deve essere non statico e pubblico, non deve restituire un valore né accettare parametri. Se inoltre si usa async-await nel metodo di test, il tipo restituito deve essere 'Task' o 'ValueTask'. + + + + Test method {0}.{1} threw exception: +{2} + Il metodo di test {0}.{1} ha generato un'eccezione: +{2} + + + + Unable to get type {0}. Error: {1}. + Non è possibile ottenere il tipo {0}. Errore: {1}. + + + + The called code threw an exception that was caught, but the exception value was null + Il codice chiamato ha generato un'eccezione che è stata rilevata, ma il valore dell'eccezione è Null + + + + {0} For UWP projects, if you are using UI objects in test consider using [UITestMethod] attribute instead of [TestMethod] to execute test in UI thread. + {0}. Se, per i progetti della piattaforma UWP, nel test si usano oggetti dell'interfaccia utente, provare a specificare l'attributo [UITestMethod] invece di [TestMethod] per eseguire il test nel thread di UI. + + + + (Failed to get the message for an exception of type {0} due to an exception.) + Non è stato possibile ottenere il messaggio per un'eccezione di tipo {0} a causa di un'eccezione. + + + + 'MSTest.TestAdapter' and 'MSTest.TestFramework' must have the same version. Found 'MSTest.TestAdapter' version '{0}' and 'MSTest.TestFramework' version '{1}'. Please make sure that the versions of 'MSTest.TestAdapter' and 'MSTest.TestFramework' NuGet packages have the same version. + 'MSTest.TestAdapter' e 'MSTest.TestFramework' devono avere la stessa versione. Trovato 'MSTest.TestAdapter' versione '{0}' e 'MSTest.TestFramework' versione '{1}'. Assicurarsi che le versioni dei pacchetti NuGet 'MSTest.TestAdapter' e 'MSTest.TestFramework' siano identiche. + + Wrong number of objects for permutation. Should be greater than zero. Il numero di oggetti non è corretto per la permutazione. Deve essere maggiore di zero. diff --git a/src/Adapter/MSTestAdapter.PlatformServices/Resources/xlf/Resource.ja.xlf b/src/Adapter/MSTestAdapter.PlatformServices/Resources/xlf/Resource.ja.xlf index 68681a9ea2..132533c564 100644 --- a/src/Adapter/MSTestAdapter.PlatformServices/Resources/xlf/Resource.ja.xlf +++ b/src/Adapter/MSTestAdapter.PlatformServices/Resources/xlf/Resource.ja.xlf @@ -2,16 +2,117 @@ + + Assembly cleanup method '{0}.{1}' timed out after {2}ms + アセンブリ クリーンアップ メソッド '{0}.{1}' が {2}ms 後にタイムアウトしました + + + + Assembly cleanup method '{0}.{1}' was canceled + アセンブリ クリーンアップ メソッド '{0}.{1}' が取り消されました + + + + Assembly initialize method '{0}.{1}' timed out after {2}ms + アセンブリ初期化メソッド '{0}.{1}' が {2}ms 後にタイムアウトになりました + + + + Assembly initialize method '{0}.{1}' was canceled + アセンブリ初期化メソッド '{0}.{1}' が取り消されました + + + + MSTestAdapterV2 + MSTestAdapterV2 + + + + Exception occurred while enumerating IDataSource attribute on "{0}.{1}": {2} + "{0}.{1}" で IDataSource 属性を列挙中に例外が発生しました: {2} + {0}: TypeName with namespace, +{1}: Method name, +{2}: Exception details + + + Exception occurred while expanding IDataSource rows from attribute on "{0}.{1}": {2} + "{0}.{1}" の属性から IDataSource 行を展開中に例外が発生しました: {2} + {0}: TypeName with namespace, +{1}: Method name, +{2}: CannotExpandIDataSourceAttribute_DuplicateDisplayName or CannotExpandIDataSourceAttribute_CannotSerialize + + + Data on index {0} for "{1}" cannot be serialized. All data provided through "IDataSource" should be serializable. If you need to test non-serializable data sources, please make sure you add "TestDataSourceDiscovery" attribute on your test assembly and set the discovery option to "DuringExecution". + "{1}" のインデックス {0} のデータをシリアル化できません。"IDataSource" を介して提供されるすべてのデータはシリアル化可能である必要があります。シリアル化できないデータ ソースをテストする必要がある場合は、テスト アセンブリに "TestDataSourceDiscovery" 属性を追加し、検出オプションを "DuringExecution" に設定してください。 + {0}: Zero based index if an element inside of an array, +{1}: Test name + + + Display name "{2}" on indexes {0} and {1} are duplicate. Display names should be unique. + インデックス {0} と {1} の表示名 "{2}" が重複しています。表示名は一意である必要があります。 + {0}, {1}: Zero based index if an element inside of an array +{2}: Test display name. + Could not find file '{0}'. ファイル '{0}' が見つかりませんでした。 + + Cannot run test method '{0}.{1}': Test data doesn't match method parameters. Either the count or types are different. +Test expected {2} parameter(s), with types '{3}', +but received {4} argument(s), with types '{5}'. + テスト メソッド '{0}.{1}' を実行できません: テスト データがメソッド パラメーターと一致しません。カウントまたは型が異なります。 +型 '{3}'、 + を持つ、予期された {2} パラメーターをテストします +ただし、型 '{5}' の引数 {4} を受け取りました。 + + + + Cannot run test method '{0}.{1}': Method has parameters, but does not define any test source. Use '[DataRow]', '[DynamicData]', or a custom 'ITestDataSource' data source to provide test data. + テスト メソッド '{0} を実行できません。{1}': メソッドにはパラメーターがありますが、テスト ソースは定義されていません。'[DataRow]'、'[DynamicData]'、カスタムの 'ITestDataSource' データ ソースを使用して、テスト データを提供します。 + + + + Class cleanup method '{0}.{1}' timed out after {2}ms + クラス クリーンアップ メソッド '{0}.{1}' が {2}ms 後にタイムアウトしました + + + + Class cleanup method '{0}.{1}' was canceled + クラス クリーンアップ メソッド '{0}.{1}' が取り消されました + + + + Class initialize method '{0}.{1}' timed out after {2}ms + クラス初期化メソッド '{0}.{1}' が {2}ms 後にタイムアウトになりました + + + + Class initialize method '{0}.{1}' was canceled + クラス初期化メソッド '{0}.{1}' が取り消されました + + The parameter should not be null or empty. パラメーターを null または空にすることはできません。 + + MSTestAdapter failed to discover tests in class '{0}' of assembly '{1}' because {2}. + MSTestAdapter でアセンブリ '{1}' のクラス '{0}' にテストが見つかりませんでした。理由 {2}。 + + + + {0} (Data Row {1}) + {0} (データ行 {1}) + + + + Debug Trace: + デバッグ トレース: + + Test Run deployment issue: Bad deployment item: '{0}': output directory '{1}' specifies the item to be deployed outside deployment root directory which is not allowed. テストの実行の配置問題です: 配置項目が正しくありません: '{0}': 出力ディレクトリ '{1}' は、項目が配置ルート ディレクトリ外に配置されるように指定していますが、それは許可されません。 @@ -77,16 +178,108 @@ 配置項目 '{0}' (出力ディレクトリ '{1}') + + [MSTest][Discovery][{0}] {1} + [MSTest][Discovery][{0}] {1} + + + + Both '.runsettings' and '.testconfig.json' files have been detected. Please select only one of these test configuration files. + '.runsettings' ファイルと '.testconfig.json' ファイルの両方が検出されました。これらのテスト構成ファイルのいずれか 1 つだけを選択してください。 + + + + {0}: {1} + {0}: {1} + + + + "{0}": (Failed to get exception description due to an exception of type "{1}". + "{0}": (種類が "{1}"の例外のため、例外の説明を取得できませんでした。 + {0}: Type of the original exception that we're trying to get the description of. +{1}: Thrown exception + + + Exceptions thrown: + スローされた例外: + This is usually precedes by TestAssembly_AssemblyDiscoveryFailure message, and preceded by list of exceptions thrown in a test discovery session. + + + Test '{0}' was canceled + テスト '{0}' が取り消されました + + + + Test '{0}' timed out after {1}ms + テスト '{0}' が {1} ミリ秒後にタイムアウトしました + + + + Getting custom attributes for type {0} threw exception (will ignore and use the reflection way): {1} + 型 {0} のカスタム属性を取得中に例外がスローされました (無視してリフレクションの方法を使用します): {1} + {0}: Attribute full type name. +{1}: Exception description + + + The type of the generic parameter '{0}' could not be inferred. + ジェネリック パラメーター '{0}' の型を推論できませんでした。 + + + + The generic test method '{0}' doesn't have arguments, so the generic parameter cannot be inferred. + ジェネリック テスト メソッド '{0}' に引数がないため、ジェネリック パラメーターを推論できません。 + + + + Found two conflicting types for generic parameter '{0}'. The conflicting types are '{1}' and '{2}'. + ジェネリック パラメーター '{0}' に 2 つの競合する型が見つかりました。競合する型は '{1}' および '{2}' です。 + + + + Invalid value '{0}' specified for 'ClassCleanupLifecycle'. Supported scopes are {1}. + 'ClassCleanupLifecycle' に無効な値 '{0}' が指定されました。サポートされているスコープは {1} です。 + {Locked="ClassCleanupLifecycle"} + + + Invalid value '{0}' specified for 'Scope'. Supported scopes are {1}. + 無効な値 '{0}' が 'Scope' に指定されました。サポートされているスコープは {1} です。 + {Locked="Scope"} + + + Invalid value '{0}' specified for 'Workers'. The value should be a non-negative integer. + 無効な値 '{0}' が 'Workers' に指定されました。値は負ではない整数である必要があります。 + {Locked="Workers"} + + + Invalid settings '{0}'. Unexpected XmlAttribute: '{1}'. + 設定 '{0}' は無効です。予期しない XmlAttribute: '{1}'。 + + MSTestAdapter encountered an unexpected element '{0}' in its settings '{1}'. Remove this element and try again. MSTestAdapter で設定 '{1}' に予期しない要素 '{0}' が見つかりました。この要素を削除して、もう一度お試しください。 + + Invalid settings '{0}'. Unexpected XmlElement: '{1}'. + 設定 '{0}' は無効です。予期しない XmlElement: '{1}'。 + + Invalid value '{0}' for runsettings entry '{1}', setting will be ignored. runsettings エントリ '{1}' の値 '{0}' は無効です。設定は無視されます。 + + Invalid value '{0}' for runsettings entry '{1}', setting will be ignored. + runsettings エントリ '{1}' の値 '{0}' は無効です。設定は無視されます。 + + + + Warning : A testsettings file or a vsmdi file is not supported with the MSTest V2 Adapter. + 警告: testsettings ファイル、vsmdi ファイルは MSTest V2 アダプターではサポートされていません。 + + Test Run deployment issue: The assembly or module '{0}' was not found. Reason: {1} テストの実行の配置問題です: アセンブリまたはモジュール '{0}' が見つかりませんでした。理由: {1} @@ -97,21 +290,309 @@ テストの実行の配置問題です: アセンブリまたはモジュール '{0}' が見つかりませんでした。 + + An older version of MSTestV2 package is loaded in assembly, test discovery might fail to discover all data tests if they depend on `.runsettings` file. + 古いバージョンの MSTestV2 パッケージがアセンブリに読み込まれています。`.runsettings` ファイルに依存している場合、テスト検出ですべてのデータ テストを検出できない可能性があります。 + + + + Runsettings entry '<ExecutionApartmentState>STA</ExecutionApartmentState>' is not supported on non-Windows OSes. + Runsettings エントリ '<ExecutionApartmentState>STA</ExecutionApartmentState>' は、Windows OS 以外ではサポートされていません。 + + + + Running tests in any of the provided sources is not supported for the selected platform + 指定されたソースのいずれかでのテストの実行は、選択されたプラットフォームでサポートされていません + + + + Failed to discover tests from assembly {0}. Reason:{1} + アセンブリ {0} からテストを検出できませんでした。理由:{1} + + + + File does not exist: {0} + ファイルが存在しません: {0} + + + + Test cleanup method '{0}.{1}' timed out after {2}ms + テスト クリーンアップ メソッド '{0}.{1}' が {2}ms 後にタイムアウトしました + + + + Test cleanup method '{0}.{1}' was canceled + テスト クリーンアップ メソッド '{0}.{1}' が取り消されました + + + + TestContext cannot be Null. + TestContext を Null にすることはできません。 + + + + TestContext Messages: + TestContext メッセージ: + + + + Test initialize method '{0}.{1}' timed out after {2}ms + テスト初期化メソッド '{0}.{1}' が {2}ms 後にタイムアウトになりました + + + + Test initialize method '{0}.{1}' was canceled + テスト初期化メソッド '{0}.{1}' が取り消されました + + + + Test method {0} was not found. + テスト メソッド {0} が見つかりませんでした。 + + + + Test Parallelization enabled for {0} (Workers: {1}, Scope: {2}) + {0} でテスト並列処理が有効にされています (Workers: {1}、Scope: {2})。 + {Locked="Workers"}{Locked="Scope"} + {0}_{1} {2} {0}_{1} {2} + + Unable to load types from the test source '{0}'. Some or all of the tests in this source may not be discovered. +Error: {1} + テスト ソース '{0}' から型を読み込むことができません。このソース内の一部またはすべてのテストが見つからない可能性があります。 +エラー: {1} + + + + Assembly Cleanup method {0}.{1} failed. Error Message: {2}. StackTrace: {3} + アセンブリ クリーンアップ メソッド {0}.{1} に失敗しました。エラー メッセージ: {2}。スタック トレース: {3} + + + + Assembly Initialization method {0}.{1} threw exception. {2}: {3}. Aborting test execution. + アセンブリ初期化メソッド {0}.{1} は例外をスローしました。{2}: {3}。テストの実行を中止しています。 + + + + Class Cleanup method {0}.{1} failed. Error Message: {2}. Stack Trace: {3} + クラス クリーンアップ メソッド {0}.{1} に失敗しました。エラー メッセージ: {2}。スタック トレース: {3} + + + + Class Initialization method {0}.{1} threw exception. {2}: {3}. + クラス初期化メソッド {0}.{1} は例外をスローしました。{2}: {3}。 + + + + Method {0}.{1} has wrong signature. The method must be static, public, does not return a value and should not take any parameter. Additionally, if you are using async-await in method then return-type must be 'Task' or 'ValueTask'. + メソッド {0}。{1} は不適切なシグネチャを含んでいます。メソッドは static および public である必要があり、値を返しません。また、パラメーターを受け取ることはできません。また、メソッドで async-await を使用している場合、戻り値の型は 'Task' または 'ValueTask' である必要があります。 + + + + Method {0}.{1} has wrong signature. The method must be static, public, does not return a value and should take a single parameter of type TestContext. Additionally, if you are using async-await in method then return-type must be 'Task' or 'ValueTask'. + メソッド {0}。{1} は不適切なシグネチャを含んでいます。メソッドは static および public である必要があり、値を返しません。また、TestContext 型の 1 つのパラメーターを受け取る必要があります。また、メソッドで async-await を使用している場合、戻り値の型は 'Task' または 'ValueTask' である必要があります。 + + + + TestCleanup method {0}.{1} threw exception. {2}. + TestCleanup メソッド {0}.{1} は例外をスローしました。{2}。 + + + + Error calling Test Cleanup method for test class {0}: {1} + テスト クラス {0} のテスト クリーンアップ メソッドの呼び出しでエラーが発生しました: {1} + + + + TestCleanup Stack Trace + TestCleanup スタック トレース + + Data source '{0}' cannot be found in the test configuration settings データ ソース '{0}' がテスト構成の設定に見つかりません + + --- End of inner exception stack trace --- + --- 内部例外スタック トレースの終わり --- + + The unit test adapter failed to connect to the data source or to read the data. For more information on troubleshooting this error, see "Troubleshooting Data-Driven Unit Tests" (http://go.microsoft.com/fwlink/?LinkId=62412) in the MSDN Library. Error details: {0} 単体テスト アダプターがデータ ソースに接続できなかったか、データを読み取れませんでした。このエラーのトラブルシューティングの詳細については、MSDN ライブラリの「方法: データ ドリブン単体テストを作成する」(http://go.microsoft.com/fwlink/?LinkId=62412) をご覧ください。エラーの詳細: {0} + + UTA031: class {0} does not have valid TestContext property. TestContext must be of type TestContext, must be non-static, and must be public. For example: public TestContext TestContext. + UTA031: クラス {0} に有効な TestContext プロパティがありません。TestContext は TestContext 型で、非静的である必要があり、public である必要があります。たとえば、public TestContext TestContext です。 + + + + UTA007: Method {1} defined in class {0} does not have correct signature. Test method marked with the [TestMethod] attribute must be non-static, public, return-type as void and should not take any parameter. Example: public void Test.Class1.Test(). Additionally, if you are using async-await in test method then return-type must be 'Task' or 'ValueTask'. Example: public async Task Test.Class1.Test2() + UTA007: クラス {0} で定義されているメソッド {1} に適切なシグネチャが含まれていません。[TestMethod] 属性でマークされたテスト メソッドは、non-static および public である必要があり、戻り値の型は void である必要があります。また、パラメーターを受け取ることはできません。例: public void Test.Class1.Test()。また、テスト メソッドで async-await を使用している場合、戻り値の型は 'Task' または 'ValueTask' である必要があります。例: public async Task Test.Class1.Test2() + + + + UTA054: {0}.{1} has invalid Timeout attribute. The timeout must be an integer value greater than 0. + UTA054: {0}。{1} に無効な Timeout 属性があります。タイムアウトには、0 より大きい整数値を指定する必要があります。 + + + + UTA014: {0}: Cannot define more than one method with the AssemblyCleanup attribute inside an assembly. + UTA014: {0}: 1 つのアセンブリ内で、AssemblyCleanup 属性を伴う 2 つ以上のメソッドを定義することはできません。 + + + + UTA013: {0}: Cannot define more than one method with the AssemblyInitialize attribute inside an assembly. + UTA013: {0}: 1 つのアセンブリ内で、AssemblyInitialize 属性を伴う 2 つ以上のメソッドを定義することはできません。 + + + + UTA026: {0}: Cannot define more than one method with the ClassCleanup attribute inside a class. + UTA026: {0}: 1 つのクラス内で、ClassCleanup 属性を伴う 2 つ以上のメソッドを定義することはできません。 + + + + UTA025: {0}: Cannot define more than one method with the ClassInitialize attribute inside a class. + UTA025: {0}: 1 つのクラス内で、ClassInitialize 属性を伴う 2 つ以上のメソッドを定義することはできません。 + + + + UTA024: {0}: Cannot define more than one method with the TestCleanup attribute. + UTA024: {0}: TestCleanup 属性を伴う 2 つ以上のメソッドを定義することはできません。 + + + + UTA018: {0}: Cannot define more than one method with the TestInitialize attribute. + UTA018: {0}: TestInitialize 属性を伴う 2 つ以上のメソッドを定義することはできません。 + + + + UTA001: TestClass attribute defined on non-public class {0} + UTA001: TestClass 属性がパブリックでないクラス {0} で定義されています + + + + UTA023: {0}: Cannot define predefined property {2} on method {1}. + UTA023: {0}: メソッド {1} 上の以前に定義されたプロパティ {2} を定義することはできません。 + + + + TestClass attribute defined on generic non-abstract class {0} + 汎用の非抽象クラス {0}で定義された TestClass 属性 + + + + UTA021: {0}: Null or empty custom property defined on method {1}. The custom property must have a valid name. + UTA021: {0}: Null または空のカスタム プロパティが、メソッド {1} で定義されています。カスタム プロパティには有効な名前を指定しなければなりません。 + + + + An unhandled exception was thrown by the 'Execute' method. Please report this error to the author of the attribute '{0}'. +{1} + 'Execute' メソッドによってハンドルされない例外がスローされました。属性 '{0}' の作成者にこのエラーを報告してください。 +{1} + + + + The ExpectedException attribute defined on test method {0}.{1} threw an exception during construction. +{2} + テスト メソッド {0}.{1} に定義されている ExpectedException 属性が、作成中に例外をスローしました。 +{2} + + + + Failed to obtain the exception thrown by test method {0}.{1}. + テスト メソッド {0}.{1} によってスローされた例外を取得できませんでした。 + + + + Initialization method {0}.{1} threw exception. {2}. + 初期化メソッド {0}.{1} は例外をスローしました。{2}。 + + + + Unable to create instance of class {0}. Error: {1}. + クラス {0} のインスタンスを作成できません。エラー: {1}。 + + + + Method {0}.{1} does not exist. + メソッド {0}.{1} は存在しません。 + + + + The test method '{0}.{1}' has multiple attributes derived from '{2}' defined on it. Only one such attribute is allowed. + テスト メソッド '{0}.{1}' には、 '{2}' から派生した属性が複数定義されています。このような属性は 1 つしか許可されません。 + + + + Error in executing test. No result returned by extension. If using extension of TestMethodAttribute then please contact vendor. + テストの実行中にエラーが発生しました。拡張から結果が返されませんでした。TestMethodAttribute の拡張クラスを使用している場合は、ベンダーに連絡してください。 + + + + Cannot find a valid constructor for test class '{0}'. Valid constructors are 'public' and either parameterless or with one parameter of type 'TestContext'. + テスト クラス '{0}' の有効なコンストラクターが見つかりません。有効なコンストラクターは、'public' で、パラメーターがないもの、または 'TestContext' 型のパラメーター 1 個を取るものです。 + + + + Unable to find property {0}.TestContext. Error:{1}. + プロパティ {0}.TestContext が見つかりません。エラー: {1}。 + + + + Unable to set TestContext property for the class {0}. Error: {1}. + クラス {0} の TestContext プロパティを設定できません。エラー: {1}。 + + + + The {0}.TestContext has incorrect type. + {0}.TestContext は不適切な型を含んでいます。 + + + + Method {0}.{1} has wrong signature. The method must be non-static, public, does not return a value and should not take any parameter. Additionally, if you are using async-await in method then return-type must be 'Task' or 'ValueTask'. + メソッド {0}。{1} は不適切なシグネチャを含んでいます。メソッドは non-static および public である必要があり、値を返しません。また、パラメーターを受け取ることはできません。また、メソッドで async-await を使用している場合、戻り値の型は 'Task' または 'ValueTask' である必要があります。 + + + + Test method {0}.{1} threw exception: +{2} + テスト メソッド {0}.{1} が例外をスローしました: +{2} + + + + Unable to get type {0}. Error: {1}. + 型 {0} を取得できません。エラー: {1}。 + + + + The called code threw an exception that was caught, but the exception value was null + 呼び出されたコードはキャッチされた例外をスローしましたが、例外値が null でした + + + + {0} For UWP projects, if you are using UI objects in test consider using [UITestMethod] attribute instead of [TestMethod] to execute test in UI thread. + {0} UWP プロジェクトについて、テスト内で UI オブジェクトを使用している場合は、[TestMethod] の代わりに [UITestMethod] 属性を使用して UI スレッド内でテストを実行することを検討してください。 + + + + (Failed to get the message for an exception of type {0} due to an exception.) + (例外が発生したため、型 {0} の例外のメッセージを取得できませんでした。) + + + + 'MSTest.TestAdapter' and 'MSTest.TestFramework' must have the same version. Found 'MSTest.TestAdapter' version '{0}' and 'MSTest.TestFramework' version '{1}'. Please make sure that the versions of 'MSTest.TestAdapter' and 'MSTest.TestFramework' NuGet packages have the same version. + 'MSTest.TestAdapter' と 'MSTest.TestFramework' は同じバージョンである必要があります。'MSTest.TestAdapter' バージョン '{0}' と 'MSTest.TestFramework' バージョン '{1}' が見つかりました。'MSTest.TestAdapter' と 'MSTest.TestFramework' の NuGet パッケージのバージョンが同じであることを確認してください。 + + Wrong number of objects for permutation. Should be greater than zero. 順列のオブジェクト数が正しくありません。ゼロより大きくなければなりません。 diff --git a/src/Adapter/MSTestAdapter.PlatformServices/Resources/xlf/Resource.ko.xlf b/src/Adapter/MSTestAdapter.PlatformServices/Resources/xlf/Resource.ko.xlf index d602b1e260..55a932381d 100644 --- a/src/Adapter/MSTestAdapter.PlatformServices/Resources/xlf/Resource.ko.xlf +++ b/src/Adapter/MSTestAdapter.PlatformServices/Resources/xlf/Resource.ko.xlf @@ -2,16 +2,116 @@ + + Assembly cleanup method '{0}.{1}' timed out after {2}ms + {2}밀리초 후 어셈블리 정리 메서드 '{0}.{1}'이(가) 시간 초과되었습니다. + + + + Assembly cleanup method '{0}.{1}' was canceled + 어셈블리 정리 메서드 '{0}.{1}'이(가) 취소되었습니다. + + + + Assembly initialize method '{0}.{1}' timed out after {2}ms + {2}밀리초 후 '{0}.{1}' 어셈블리 초기화 메서드의 시간이 초과되었습니다. + + + + Assembly initialize method '{0}.{1}' was canceled + 어셈블리 초기화 메서드 '{0}.{1}'(이)가 취소되었습니다. + + + + MSTestAdapterV2 + MSTestAdapterV2 + + + + Exception occurred while enumerating IDataSource attribute on "{0}.{1}": {2} + "{0}.{1}"에서 IDataSource 특성을 열거하는 동안 예외가 발생했습니다. {2} + {0}: TypeName with namespace, +{1}: Method name, +{2}: Exception details + + + Exception occurred while expanding IDataSource rows from attribute on "{0}.{1}": {2} + "{0}.{1}"의 특성에서 IDataSource 행을 확장하는 동안 예외가 발생했습니다. {2} + {0}: TypeName with namespace, +{1}: Method name, +{2}: CannotExpandIDataSourceAttribute_DuplicateDisplayName or CannotExpandIDataSourceAttribute_CannotSerialize + + + Data on index {0} for "{1}" cannot be serialized. All data provided through "IDataSource" should be serializable. If you need to test non-serializable data sources, please make sure you add "TestDataSourceDiscovery" attribute on your test assembly and set the discovery option to "DuringExecution". + "{1}"에 대한 {0} 인덱스의 데이터를 직렬화할 수 없습니다. "IDataSource"를 통해 제공된 모든 데이터를 직렬화할 수 있어야 합니다. 직렬화할 수 없는 데이터 원본을 테스트해야 하는 경우 테스트 어셈블리에 "TestDataSourceDiscovery" 특성을 추가하고 검색 옵션을 "DuringExecution"으로 설정하세요. + {0}: Zero based index if an element inside of an array, +{1}: Test name + + + Display name "{2}" on indexes {0} and {1} are duplicate. Display names should be unique. + {0} 및 {1} 인덱스의 "{2}" 표시 이름이 중복됩니다. 표시 이름은 고유해야 합니다. + {0}, {1}: Zero based index if an element inside of an array +{2}: Test display name. + Could not find file '{0}'. '{0}' 파일을 찾을 수 없습니다. + + Cannot run test method '{0}.{1}': Test data doesn't match method parameters. Either the count or types are different. +Test expected {2} parameter(s), with types '{3}', +but received {4} argument(s), with types '{5}'. + 테스트 메서드 '{0}{1}'을(를) 실행할 수 없음: 테스트 데이터가 메서드 매개 변수와 일치하지 않습니다. 개수 또는 형식이 다릅니다. +테스트에 {2} 매개 변수로 ‘{3}’ 형식이 필요하지만, +{4} 인수의 ‘{5}’ 형식을 받았습니다. + + + + Cannot run test method '{0}.{1}': Method has parameters, but does not define any test source. Use '[DataRow]', '[DynamicData]', or a custom 'ITestDataSource' data source to provide test data. + 테스트 메서드 '{0}{1}'을(를) 실행할 수 없음: 메서드에 매개 변수가 있지만 테스트 원본을 정의하지 않습니다. '[DataRow]', '[DynamicData]' 또는 사용자 지정 'ITestDataSource' 데이터 원본을 사용하여 테스트 데이터를 제공합니다. + + + + Class cleanup method '{0}.{1}' timed out after {2}ms + {2}밀리초 후 클래스 정리 메서드 '{0}.{1}'이(가) 시간 초과되었습니다. + + + + Class cleanup method '{0}.{1}' was canceled + 클래스 정리 메서드 '{0}.{1}'이(가) 취소되었습니다. + + + + Class initialize method '{0}.{1}' timed out after {2}ms + {2}밀리초 후 '{0}.{1}' 클래스 초기화 메서드의 시간이 초과되었습니다. + + + + Class initialize method '{0}.{1}' was canceled + '클래스 초기화 메서드 '{0}.{1}'이(가) 취소되었습니다. + + The parameter should not be null or empty. 매개 변수는 null이거나 비워 둘 수 없습니다. + + MSTestAdapter failed to discover tests in class '{0}' of assembly '{1}' because {2}. + MSTestAdapter가 {2} 때문에 어셈블리 '{1}'의 클래스 '{0}'에서 테스트를 검색하지 못했습니다. + + + + {0} (Data Row {1}) + {0}(데이터 행 {1}) + + + + Debug Trace: + 디버그 추적: + + Test Run deployment issue: Bad deployment item: '{0}': output directory '{1}' specifies the item to be deployed outside deployment root directory which is not allowed. 테스트 실행 배포 문제: 잘못된 배포 항목: '{0}': 출력 디렉터리 '{1}'에서 항목을 배포 루트 디렉터리 외부에 배포하도록 지정하며 이는 허용되지 않습니다. @@ -77,16 +177,108 @@ 배포 항목 '{0}'(출력 디렉터리 '{1}') + + [MSTest][Discovery][{0}] {1} + [MSTest][검색][{0}] {1} + + + + Both '.runsettings' and '.testconfig.json' files have been detected. Please select only one of these test configuration files. + '.runsettings' 및 '.testconfig.json' 파일이 모두 검색되었습니다. 이러한 테스트 구성 파일 중 하나만 선택하세요. + + + + {0}: {1} + {0}: {1} + + + + "{0}": (Failed to get exception description due to an exception of type "{1}". + "{0}": ("{1}" 형식의 예외로 인해 예외 설명을 가져오지 못했습니다. + {0}: Type of the original exception that we're trying to get the description of. +{1}: Thrown exception + + + Exceptions thrown: + 예외 발생: + This is usually precedes by TestAssembly_AssemblyDiscoveryFailure message, and preceded by list of exceptions thrown in a test discovery session. + + + Test '{0}' was canceled + 테스트 '{0}'이(가) 취소되었습니다. + + + + Test '{0}' timed out after {1}ms + 테스트 '{0}'이(가) {1}밀리초 후에 시간 초과되었습니다. + + + + Getting custom attributes for type {0} threw exception (will ignore and use the reflection way): {1} + {0} 형식에 대한 사용자 지정 특성을 가져오는 데 예외가 발생했습니다(무시하고 리플렉션 방법 사용). {1} + {0}: Attribute full type name. +{1}: Exception description + + + The type of the generic parameter '{0}' could not be inferred. + 제네릭 매개 변수 '{0}'의 형식을 유추할 수 없습니다. + + + + The generic test method '{0}' doesn't have arguments, so the generic parameter cannot be inferred. + 제네릭 테스트 메서드 '{0}'에 인수가 없으므로 제네릭 매개 변수를 유추할 수 없습니다. + + + + Found two conflicting types for generic parameter '{0}'. The conflicting types are '{1}' and '{2}'. + 제네릭 매개 변수 '{0}'에 대해 충돌하는 두 가지 형식을 찾았습니다. 충돌하는 형식은 '{1}' 및 ''{2}입니다. + + + + Invalid value '{0}' specified for 'ClassCleanupLifecycle'. Supported scopes are {1}. + 'ClassCleanupLifecycle'에 대해 '{0}' 잘못된 값이 지정되었습니다. 지원되는 범위는 {1}입니다. + {Locked="ClassCleanupLifecycle"} + + + Invalid value '{0}' specified for 'Scope'. Supported scopes are {1}. + 'Scope'에 대해 잘못된 값 '{0}'이(가) 지정되었습니다. 지원되는 범위는 {1}입니다. + {Locked="Scope"} + + + Invalid value '{0}' specified for 'Workers'. The value should be a non-negative integer. + 'Workers'에 대해 잘못된 값 '{0}'이(가) 지정되었습니다. 이 값은 음수가 아닌 정수여야 합니다. + {Locked="Workers"} + + + Invalid settings '{0}'. Unexpected XmlAttribute: '{1}'. + '{0}' 설정이 잘못되었습니다. 예기치 않은 XmlAttribute '{1}'이(가) 있습니다. + + MSTestAdapter encountered an unexpected element '{0}' in its settings '{1}'. Remove this element and try again. MSTestAdapter의 '{1}' 설정에서 예기치 않은 요소 '{0}'이(가) 발견되었습니다. 이 요소를 제거하고 다시 시도하세요. + + Invalid settings '{0}'. Unexpected XmlElement: '{1}'. + '{0}' 설정이 잘못되었습니다. 예기치 않은 XmlElement '{1}'이(가) 있습니다. + + Invalid value '{0}' for runsettings entry '{1}', setting will be ignored. runsettings 항목 '{1}'에 대해 '{0}' 값이 잘못되었습니다. 설정은 무시됩니다. + + Invalid value '{0}' for runsettings entry '{1}', setting will be ignored. + runsettings 항목 '{1}'에 대해 '{0}' 값이 잘못되었습니다. 설정은 무시됩니다. + + + + Warning : A testsettings file or a vsmdi file is not supported with the MSTest V2 Adapter. + 경고: MSTest V2 어댑터에서는 testsettings 파일 또는 vsmdi 파일이 지원되지 않습니다. + + Test Run deployment issue: The assembly or module '{0}' was not found. Reason: {1} 테스트 실행 배포 문제: 어셈블리 또는 모듈 '{0}'을(를) 찾을 수 없습니다. 이유: {1} @@ -97,21 +289,309 @@ 테스트 실행 배포 문제: 어셈블리 또는 모듈 '{0}'을(를) 찾을 수 없습니다. + + An older version of MSTestV2 package is loaded in assembly, test discovery might fail to discover all data tests if they depend on `.runsettings` file. + 이전 버전 MSTestV2 패키지가 어셈블리에 로드되어 테스트 검색이 '.runsettings' 파일에 종속된 경우 모든 데이터 테스트를 검색하지 못할 수 있습니다. + + + + Runsettings entry '<ExecutionApartmentState>STA</ExecutionApartmentState>' is not supported on non-Windows OSes. + Runsettings 항목 '<ExecutionApartmentState>STA</ExecutionApartmentState>'는 Windows 이외의 OS에서는 지원되지 않습니다. + + + + Running tests in any of the provided sources is not supported for the selected platform + 선택된 플랫폼의 경우 제공된 소스에서 테스트를 실행할 수 없습니다. + + + + Failed to discover tests from assembly {0}. Reason:{1} + 어셈블리 {0}에서 테스트를 검색하지 못했습니다. 이유:{1} + + + + File does not exist: {0} + 파일이 없습니다. {0} + + + + Test cleanup method '{0}.{1}' timed out after {2}ms + {2}밀리초 후 테스트 정리 메서드 '{0}.{1}'이(가) 시간 초과되었습니다. + + + + Test cleanup method '{0}.{1}' was canceled + 테스트 정리 메서드 '{0}.{1}'이(가) 취소되었습니다. + + + + TestContext cannot be Null. + TestContext는 null일 수 없습니다. + + + + TestContext Messages: + TestContext 메시지: + + + + Test initialize method '{0}.{1}' timed out after {2}ms + {2}밀리초 후 '{0}.{1}' 테스트 초기화 메서드의 시간이 초과되었습니다. + + + + Test initialize method '{0}.{1}' was canceled + 테스트 초기화 메서드 '{0}.{1}'이(가) 취소되었습니다. + + + + Test method {0} was not found. + {0} 테스트 메서드를 찾을 수 없습니다. + + + + Test Parallelization enabled for {0} (Workers: {1}, Scope: {2}) + {0}에 대해 테스트 병렬 처리를 사용합니다(Workers: {1}, Scope: {2}). + {Locked="Workers"}{Locked="Scope"} + {0}_{1} {2} {0}_{1} {2} + + Unable to load types from the test source '{0}'. Some or all of the tests in this source may not be discovered. +Error: {1} + 테스트 소스 '{0}'에서 형식을 로드할 수 없습니다. 이 소스의 일부 또는 모든 테스트를 검색할 수 없습니다. +오류: {1} + + + + Assembly Cleanup method {0}.{1} failed. Error Message: {2}. StackTrace: {3} + 어셈블리 정리 메서드 {0}.{1}이(가) 실패했습니다. 오류 메시지: {2}. StackTrace: {3} + + + + Assembly Initialization method {0}.{1} threw exception. {2}: {3}. Aborting test execution. + 어셈블리 초기화 메서드 {0}.{1}에서 예외를 throw했습니다. {2}: {3}. 테스트 실행을 중단합니다. + + + + Class Cleanup method {0}.{1} failed. Error Message: {2}. Stack Trace: {3} + 클래스 정리 메서드 {0}.{1}이(가) 실패했습니다. 오류 메시지: {2}. 스택 추적: {3} + + + + Class Initialization method {0}.{1} threw exception. {2}: {3}. + 클래스 초기화 메서드 {0}.{1}에서 예외를 throw했습니다. {2}: {3} + + + + Method {0}.{1} has wrong signature. The method must be static, public, does not return a value and should not take any parameter. Additionally, if you are using async-await in method then return-type must be 'Task' or 'ValueTask'. + {0}.{1} 메서드의 서명이 잘못되었습니다. 메서드는 정적이고 공용이어야 하며, 값을 반환하거나 매개 변수를 취하지 않습니다. 또한 메서드에서 비동기 대기를 사용하는 경우 반환 형식은 'Task' 또는 'ValueTask'여야 합니다. + + + + Method {0}.{1} has wrong signature. The method must be static, public, does not return a value and should take a single parameter of type TestContext. Additionally, if you are using async-await in method then return-type must be 'Task' or 'ValueTask'. + {0}.{1} 메서드의 서명이 잘못되었습니다. 메서드는 static, public이어야 하고, 값을 반환하지 않으며, TestContext 형식의 단일 매개 변수를 사용해야 합니다. 또한 메서드에서 비동기 대기를 사용하는 경우 반환 형식은 'Task' 또는 'ValueTask'여야 합니다. + + + + TestCleanup method {0}.{1} threw exception. {2}. + TestCleanup 메서드 {0}.{1}에서 예외가 발생했습니다. {2}. + + + + Error calling Test Cleanup method for test class {0}: {1} + 테스트 클래스 {0}에 대한 테스트 정리 메서드를 호출하는 오류: {1} + + + + TestCleanup Stack Trace + TestCleanup 스택 추적 + + Data source '{0}' cannot be found in the test configuration settings 테스트 구성 설정에서 데이터 원본 '{0}'을(를) 찾을 수 없습니다. + + --- End of inner exception stack trace --- + --- 내부 예외 스택 추적의 끝 --- + + The unit test adapter failed to connect to the data source or to read the data. For more information on troubleshooting this error, see "Troubleshooting Data-Driven Unit Tests" (http://go.microsoft.com/fwlink/?LinkId=62412) in the MSDN Library. Error details: {0} 단위 테스트 어댑터가 데이터 원본에 연결하거나 데이터를 읽지 못했습니다. 이 오류를 해결하는 방법에 대한 자세한 내용은 MSDN 라이브러리에서 "데이터 기반 단위 테스트 문제 해결"(http://go.microsoft.com/fwlink/?LinkId=62412)을 참조하십시오. 오류 세부 정보: {0} + + UTA031: class {0} does not have valid TestContext property. TestContext must be of type TestContext, must be non-static, and must be public. For example: public TestContext TestContext. + UTA031: {0} 클래스에 유효한 TestContext 속성이 없습니다. TestContext는 TestContext 유형이어야 하고, 정적이 아니어야 하며, 일반적이어야 합니다. 예: public TestContext TestContext. + + + + UTA007: Method {1} defined in class {0} does not have correct signature. Test method marked with the [TestMethod] attribute must be non-static, public, return-type as void and should not take any parameter. Example: public void Test.Class1.Test(). Additionally, if you are using async-await in test method then return-type must be 'Task' or 'ValueTask'. Example: public async Task Test.Class1.Test2() + UTA007: 클래스 {0}에 정의된 메서드 {1}에 올바른 서명이 없습니다. [TestMethod] 특성으로 표시된 테스트 메서드는 non-static, public, return-type이 void여야 하며 매개 변수를 사용하지 않아야 합니다. 예: public void Test.Class1.Test(). 또한 테스트 메서드에서 비동기 대기를 사용하는 경우 반환 형식은 Task여야 합니다. 예: public async Task Test.Class1.Test2() + + + + UTA054: {0}.{1} has invalid Timeout attribute. The timeout must be an integer value greater than 0. + UTA054: {0}.{1}에 잘못된 Timeout 특성이 있습니다. 시간 제한 값은 0보다 큰 정수여야 합니다. + + + + UTA014: {0}: Cannot define more than one method with the AssemblyCleanup attribute inside an assembly. + UTA014: {0}: 어셈블리 내부에서 AssemblyCleanup 특성을 사용하는 메서드를 여러 개 정의할 수 없습니다. + + + + UTA013: {0}: Cannot define more than one method with the AssemblyInitialize attribute inside an assembly. + UTA013: {0}: 어셈블리 내부에서 AssemblyInitialize 특성을 사용하는 메서드를 여러 개 정의할 수 없습니다. + + + + UTA026: {0}: Cannot define more than one method with the ClassCleanup attribute inside a class. + UTA026: {0}: 클래스 내부에서 ClassCleanup 특성을 사용하는 메서드를 여러 개 정의할 수 없습니다. + + + + UTA025: {0}: Cannot define more than one method with the ClassInitialize attribute inside a class. + UTA025: {0}: 클래스 내부에서 ClassInitialize 특성을 사용하는 메서드를 여러 개 정의할 수 없습니다. + + + + UTA024: {0}: Cannot define more than one method with the TestCleanup attribute. + UTA024: {0}: TestCleanup 특성을 사용하는 메서드를 여러 개 정의할 수 없습니다. + + + + UTA018: {0}: Cannot define more than one method with the TestInitialize attribute. + UTA018: {0}: TestInitialize 특성을 사용하는 메서드를 여러 개 정의할 수 없습니다. + + + + UTA001: TestClass attribute defined on non-public class {0} + UTA001: public이 아닌 클래스 {0}에서 TestClass 특성을 정의했습니다. + + + + UTA023: {0}: Cannot define predefined property {2} on method {1}. + UTA023: {0}: {1} 메서드에서 미리 정의된 속성 {2}을(를) 정의할 수 없습니다. + + + + TestClass attribute defined on generic non-abstract class {0} + 일반 비추상 클래스 {0}에 정의된 TestClass 속성 + + + + UTA021: {0}: Null or empty custom property defined on method {1}. The custom property must have a valid name. + UTA021: {0}: {1} 메서드에서 Null 또는 빈 사용자 지정 속성을 정의했습니다. 사용자 지정 속성에는 올바른 이름이 지정되어 있어야 합니다. + + + + An unhandled exception was thrown by the 'Execute' method. Please report this error to the author of the attribute '{0}'. +{1} + 'Execute' 메서드에서 처리되지 않은 예외가 발생했습니다. '{0}' 특성의 작성자에게 이 오류를 보고하세요. +{1} + + + + The ExpectedException attribute defined on test method {0}.{1} threw an exception during construction. +{2} + 테스트 메서드 {0}.{1}에 정의된 ExpectedException 특성이 생성하는 동안 예외를 throw했습니다. +{2} + + + + Failed to obtain the exception thrown by test method {0}.{1}. + 테스트 메서드 {0}.{1}에서 throw한 예외를 가져오지 못했습니다. + + + + Initialization method {0}.{1} threw exception. {2}. + 초기화 메서드 {0}.{1}에서 예외를 throw했습니다. {2}. + + + + Unable to create instance of class {0}. Error: {1}. + {0} 클래스의 인스턴스를 만들 수 없습니다. 오류: {1} + + + + Method {0}.{1} does not exist. + {0}.{1} 메서드가 없습니다. + + + + The test method '{0}.{1}' has multiple attributes derived from '{2}' defined on it. Only one such attribute is allowed. + 테스트 메서드 '{0}.{1}'에 {2}에서 파생된 여러 특성이 정의되어 있습니다. 이러한 특성은 하나만 허용됩니다. + + + + Error in executing test. No result returned by extension. If using extension of TestMethodAttribute then please contact vendor. + 테스트를 실행하는 중에 오류가 발생했습니다. 확장에서 결과가 반환되지 않았습니다. TestMethodAttribute 확장을 사용하는 경우 공급업체에 문의하세요. + + + + Cannot find a valid constructor for test class '{0}'. Valid constructors are 'public' and either parameterless or with one parameter of type 'TestContext'. + 테스트 클래스 '{0}'에 대한 유효한 생성자를 찾을 수 없습니다. 유효한 생성자는 'public'이며 매개 변수가 없거나 'TestContext' 유형의 매개 변수가 하나 있습니다. + + + + Unable to find property {0}.TestContext. Error:{1}. + {0}.TestContext 속성을 찾을 수 없습니다. 오류: {1} + + + + Unable to set TestContext property for the class {0}. Error: {1}. + {0} 클래스에 대해 TestContext 속성을 설정할 수 없습니다. 오류: {1} + + + + The {0}.TestContext has incorrect type. + {0}.TestContext의 형식이 잘못되었습니다. + + + + Method {0}.{1} has wrong signature. The method must be non-static, public, does not return a value and should not take any parameter. Additionally, if you are using async-await in method then return-type must be 'Task' or 'ValueTask'. + {0}.{1} 메서드의 서명이 잘못되었습니다. 메서드는 정적이 아니고 공용이어야 하며, 값을 반환하거나 매개 변수를 사용할 수 없습니다. 또한 메서드에서 비동기 대기를 사용하는 경우 반환 형식은 'Task' 또는 'ValueTask'여야 합니다. + + + + Test method {0}.{1} threw exception: +{2} + 테스트 메서드 {0}.{1}에서 예외를 throw했습니다. +{2} + + + + Unable to get type {0}. Error: {1}. + {0} 형식을 가져올 수 없습니다. 오류: {1} + + + + The called code threw an exception that was caught, but the exception value was null + 호출된 코드에서 확인된 예외가 발생했지만 예외 값이 null입니다. + + + + {0} For UWP projects, if you are using UI objects in test consider using [UITestMethod] attribute instead of [TestMethod] to execute test in UI thread. + {0} UWP 프로젝트의 경우 테스트에서 UI 개체를 사용 중이면 [TestMethod] 대신 [UITestMethod] 특성을 사용하여 UI 스레드에서 테스트를 실행하세요. + + + + (Failed to get the message for an exception of type {0} due to an exception.) + (예외로 인해 {0} 형식의 예외에 대한 메시지를 가져오지 못했습니다.) + + + + 'MSTest.TestAdapter' and 'MSTest.TestFramework' must have the same version. Found 'MSTest.TestAdapter' version '{0}' and 'MSTest.TestFramework' version '{1}'. Please make sure that the versions of 'MSTest.TestAdapter' and 'MSTest.TestFramework' NuGet packages have the same version. + 'MSTest.TestAdapter'와 'MSTest.TestFramework'의 버전이 같아야 합니다. 'MSTest.TestAdapter'의 버전은 '{0}'이고, 'MSTest.TestFramework'의 버전은 '{1}'입니다. 'MSTest.TestAdapter'와 'MSTest.TestFramework' NuGet 패키지의 버전이 동일한지 확인하세요. + + Wrong number of objects for permutation. Should be greater than zero. 순열의 개체 수가 잘못되었습니다. 개체 수는 0보다 커야 합니다. diff --git a/src/Adapter/MSTestAdapter.PlatformServices/Resources/xlf/Resource.pl.xlf b/src/Adapter/MSTestAdapter.PlatformServices/Resources/xlf/Resource.pl.xlf index 56730517f6..431f757dd8 100644 --- a/src/Adapter/MSTestAdapter.PlatformServices/Resources/xlf/Resource.pl.xlf +++ b/src/Adapter/MSTestAdapter.PlatformServices/Resources/xlf/Resource.pl.xlf @@ -2,16 +2,116 @@ + + Assembly cleanup method '{0}.{1}' timed out after {2}ms + Metoda oczyszczania zestawu „{0}.{1}” przekroczyła limit czasu po {2}ms + + + + Assembly cleanup method '{0}.{1}' was canceled + Anulowano metodę oczyszczania 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 + + + + Assembly initialize method '{0}.{1}' was canceled + Anulowano metodę inicjowania zestawu „{0}.{1}” + + + + MSTestAdapterV2 + MSTestAdapterV2 + + + + Exception occurred while enumerating IDataSource attribute on "{0}.{1}": {2} + Wystąpił wyjątek podczas wyliczania atrybutu IDataSource w przestrzeni nazwy „{0}.{1}”: {2} + {0}: TypeName with namespace, +{1}: Method name, +{2}: Exception details + + + Exception occurred while expanding IDataSource rows from attribute on "{0}.{1}": {2} + Wystąpił wyjątek podczas rozwijania wierszy IDataSource z atrybutu w przestrzeni nazw „{0}.{1}”: {2} + {0}: TypeName with namespace, +{1}: Method name, +{2}: CannotExpandIDataSourceAttribute_DuplicateDisplayName or CannotExpandIDataSourceAttribute_CannotSerialize + + + Data on index {0} for "{1}" cannot be serialized. All data provided through "IDataSource" should be serializable. If you need to test non-serializable data sources, please make sure you add "TestDataSourceDiscovery" attribute on your test assembly and set the discovery option to "DuringExecution". + Nie można serializować danych w indeksie {0} dla „{1}”. Wszystkie dane dostarczane za pomocą źródła „IDataSource” powinny być możliwe do serializacji. Jeśli chcesz przetestować nieserializowalne źródła danych, upewnij się, że dodasz atrybut „ TestDataSourceDiscovery” w zestawie testowym i ustaw opcję odnajdywania na wartość „DuringExecution”. + {0}: Zero based index if an element inside of an array, +{1}: Test name + + + Display name "{2}" on indexes {0} and {1} are duplicate. Display names should be unique. + Nazwa wyświetlana „{2}” w indeksach {0} i {1} jest duplikatem. Nazwy wyświetlane powinny być unikatowe. + {0}, {1}: Zero based index if an element inside of an array +{2}: Test display name. + Could not find file '{0}'. Nie można odnaleźć pliku „{0}”. + + Cannot run test method '{0}.{1}': Test data doesn't match method parameters. Either the count or types are different. +Test expected {2} parameter(s), with types '{3}', +but received {4} argument(s), with types '{5}'. + Nie można uruchomić metody testowej „{0}.{1}”: dane testowe nie są zgodne z parametrami metody. Liczba lub typy są różne. +Testowanie oczekiwał parametrów {2} z typami „{3}”, +ale odebrał argumenty {4} z typami „{5}”. + + + + Cannot run test method '{0}.{1}': Method has parameters, but does not define any test source. Use '[DataRow]', '[DynamicData]', or a custom 'ITestDataSource' data source to provide test data. + Nie można uruchomić metody testowej „{0}.{1}”: metoda ma parametry, ale nie definiuje żadnego źródła testowego. Użyj źródła danych „[DataRow]”, „[DynamicData]” lub niestandardowego źródła danych „ITestDataSource”, aby dostarczyć dane testowe. + + + + Class cleanup method '{0}.{1}' timed out after {2}ms + Metoda oczyszczania klasy „{0}.{1}” przekroczyła limit czasu po {2}ms + + + + Class cleanup method '{0}.{1}' was canceled + Anulowano metodę oczyszczania 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 + + + + Class initialize method '{0}.{1}' was canceled + Anulowano metodę inicjowania klasy „{0}.{1}” + + The parameter should not be null or empty. Parametr nie może mieć wartości null ani być pusty. + + MSTestAdapter failed to discover tests in class '{0}' of assembly '{1}' because {2}. + Adapter MSTestAdapter nie mógł odnaleźć testów w klasie „{0}” zestawu „{1}”, przyczyna: {2}. + + + + {0} (Data Row {1}) + {0} (wiersz danych {1}) + + + + Debug Trace: + Ślad debugowania: + + Test Run deployment issue: Bad deployment item: '{0}': output directory '{1}' specifies the item to be deployed outside deployment root directory which is not allowed. Problem wdrażania przebiegu testu: nieprawidłowy element wdrożenia: „{0}”: katalog wyjściowy „{1}” określa element do wdrożenia poza głównym katalogiem wdrożenia. Taka sytuacja jest niedozwolona. @@ -77,16 +177,108 @@ element wdrożenia „{0}” (katalog wyjściowy „{1}”) + + [MSTest][Discovery][{0}] {1} + [MSTest][Discovery][{0}] {1} + + + + Both '.runsettings' and '.testconfig.json' files have been detected. Please select only one of these test configuration files. + Wykryto zarówno pliki „.runsettings”, jak i „.testconfig.json”. Wybierz tylko jeden z tych plików konfiguracji testu. + + + + {0}: {1} + {0}: {1} + + + + "{0}": (Failed to get exception description due to an exception of type "{1}". + „{0}”: (Nie można uzyskać opisu wyjątku z powodu wyjątku typu „{1}”. + {0}: Type of the original exception that we're trying to get the description of. +{1}: Thrown exception + + + Exceptions thrown: + Zgłoszone wyjątki: + This is usually precedes by TestAssembly_AssemblyDiscoveryFailure message, and preceded by list of exceptions thrown in a test discovery session. + + + Test '{0}' was canceled + Test „{0}” został anulowany + + + + Test '{0}' timed out after {1}ms + Upłynął limit czasu testu „{0}” po {1}ms + + + + Getting custom attributes for type {0} threw exception (will ignore and use the reflection way): {1} + Pobieranie atrybutów niestandardowych dla typu {0} zgłosiło wyjątek (zignoruje i użyje sposobu odbicia): {1} + {0}: Attribute full type name. +{1}: Exception description + + + The type of the generic parameter '{0}' could not be inferred. + Nie można wywnioskować typu parametru ogólnego „{0}”. + + + + The generic test method '{0}' doesn't have arguments, so the generic parameter cannot be inferred. + Ogólna metoda testowa „{0}” nie ma argumentów, więc nie można wywnioskować parametru ogólnego. + + + + Found two conflicting types for generic parameter '{0}'. The conflicting types are '{1}' and '{2}'. + Znaleziono dwa typy powodujące konflikt dla parametru ogólnego „{0}”. Typy powodujące konflikt to „{1}” i „{2}”. + + + + Invalid value '{0}' specified for 'ClassCleanupLifecycle'. Supported scopes are {1}. + Dla ustawienia „ClassCleanupLifecycle” określono nieprawidłową wartość „{0}”. Obsługiwane zakresy to {1}. + {Locked="ClassCleanupLifecycle"} + + + Invalid value '{0}' specified for 'Scope'. Supported scopes are {1}. + Określono nieprawidłową wartość „{0}” dla właściwości „Scope”. Obsługiwane zakresy to {1}. + {Locked="Scope"} + + + Invalid value '{0}' specified for 'Workers'. The value should be a non-negative integer. + Określono nieprawidłową wartość „{0}” dla właściwości „Workers”. Wartość musi być nieujemną liczbą całkowitą. + {Locked="Workers"} + + + Invalid settings '{0}'. Unexpected XmlAttribute: '{1}'. + Nieprawidłowe ustawienia „{0}”. Nieoczekiwany atrybut XmlAttribute: „{1}”. + + MSTestAdapter encountered an unexpected element '{0}' in its settings '{1}'. Remove this element and try again. Adapter MSTestAdapter napotkał nieoczekiwany element „{0}” w ustawieniach „{1}”. Usuń ten element i spróbuj ponownie. + + Invalid settings '{0}'. Unexpected XmlElement: '{1}'. + Nieprawidłowe ustawienia „{0}”. Nieoczekiwany atrybut XmlElement: „{1}”. + + Invalid value '{0}' for runsettings entry '{1}', setting will be ignored. Nieprawidłowa wartość „{0}” dla wpisu runsettings „{1}”. Ustawienie zostanie zignorowane. + + Invalid value '{0}' for runsettings entry '{1}', setting will be ignored. + Nieprawidłowa wartość „{0}” dla wpisu runsettings „{1}”. Ustawienie zostanie zignorowane. + + + + Warning : A testsettings file or a vsmdi file is not supported with the MSTest V2 Adapter. + Ostrzeżenie: Plik testsettings lub plik vsmdi nie jest obsługiwany przez adapter MSTest w wersji 2. + + Test Run deployment issue: The assembly or module '{0}' was not found. Reason: {1} Problem wdrażania przebiegu testu: nie znaleziono zestawu lub modułu „{0}”. Przyczyna: {1} @@ -97,21 +289,309 @@ Problem wdrażania przebiegu testu: nie znaleziono zestawu lub modułu „{0}”. + + An older version of MSTestV2 package is loaded in assembly, test discovery might fail to discover all data tests if they depend on `.runsettings` file. + Starsza wersja pakietu MSTestV2 jest załadowana do zestawu. Odnajdywanie testów może nie odnaleźć wszystkich testów danych, jeśli zależą od pliku „.runsettings”. + + + + Runsettings entry '<ExecutionApartmentState>STA</ExecutionApartmentState>' is not supported on non-Windows OSes. + Wpis runsettings „<ExecutionApartmentState>STA</ExecutionApartmentState>” nie jest obsługiwany w systemach operacyjnych innych niż Windows. + + + + Running tests in any of the provided sources is not supported for the selected platform + Uruchamianie testów w żadnym z podanych źródeł nie jest obsługiwane dla wybranej platformy + + + + Failed to discover tests from assembly {0}. Reason:{1} + Nie można odnaleźć testów z zestawu {0}. Przyczyna:{1} + + + + File does not exist: {0} + Plik nie istnieje: {0} + + + + Test cleanup method '{0}.{1}' timed out after {2}ms + Metoda oczyszczania testu „{0}.{1}” przekroczyła limit czasu po {2}ms + + + + Test cleanup method '{0}.{1}' was canceled + Anulowano metodę oczyszczania testu „{0}.{1}” + + + + TestContext cannot be Null. + Wartość TestContext nie może być równa null. + + + + TestContext Messages: + Komunikaty TestContext: + + + + Test initialize method '{0}.{1}' timed out after {2}ms + Metoda inicjalizacji testu „{0}.{1}” przekroczyła limit czasu po {2}ms + + + + Test initialize method '{0}.{1}' was canceled + Anulowano metodę inicjowania testu „{0}.{1}” + + + + Test method {0} was not found. + Nie znaleziono metody testowej {0}. + + + + Test Parallelization enabled for {0} (Workers: {1}, Scope: {2}) + Przetwarzanie równoległe testów włączono dla {0} (Workers: {1}, Scope: {2}) + {Locked="Workers"}{Locked="Scope"} + {0}_{1} {2} {0}_{1} {2} + + Unable to load types from the test source '{0}'. Some or all of the tests in this source may not be discovered. +Error: {1} + Nie można załadować typów ze źródła testów „{0}”. Niektóre lub wszystkie testy w tym źródle mogły nie zostać odnalezione. +Błąd: {1} + + + + Assembly Cleanup method {0}.{1} failed. Error Message: {2}. StackTrace: {3} + Metoda czyszczenia zestawu {0}.{1} nie powiodła się. Komunikat o błędzie: {2}. Ślad stosu: {3} + + + + Assembly Initialization method {0}.{1} threw exception. {2}: {3}. Aborting test execution. + Metoda inicjująca zestaw {0}.{1} nie powiodła się. {2}: {3}. Przerywanie wykonywania testu. + + + + Class Cleanup method {0}.{1} failed. Error Message: {2}. Stack Trace: {3} + Metoda czyszczenia klasy {0}.{1} nie powiodła się. Komunikat o błędzie: {2}. Ślad stosu: {3} + + + + Class Initialization method {0}.{1} threw exception. {2}: {3}. + Metoda inicjowania klasy {0}.{1} zgłosiła wyjątek. {2}: {3}. + + + + Method {0}.{1} has wrong signature. The method must be static, public, does not return a value and should not take any parameter. Additionally, if you are using async-await in method then return-type must be 'Task' or 'ValueTask'. + Metoda {0}.{1}ma nieprawidłową sygnaturę. Metoda musi być statyczna, publiczna, nie może zwracać wartości i nie powinna przyjmować żadnego parametru. Ponadto jeśli w metodzie jest używane oczekiwanie asynchroniczne, wtedy zwracanym typem musi być typ „Task” lub „ValueTask”. + + + + Method {0}.{1} has wrong signature. The method must be static, public, does not return a value and should take a single parameter of type TestContext. Additionally, if you are using async-await in method then return-type must be 'Task' or 'ValueTask'. + Metoda {0}.{1}ma nieprawidłową sygnaturę. Metoda musi być statyczna, publiczna, nie może zwracać wartości i powinna mieć jeden parametr typu TestContext. Ponadto jeśli w metodzie jest używane oczekiwanie asynchroniczne, wtedy zwracanym typem musi być typ „Task” lub „ValueTask”. + + + + TestCleanup method {0}.{1} threw exception. {2}. + Metoda TestCleanup {0}.{1} zgłosiła wyjątek {2}. + + + + Error calling Test Cleanup method for test class {0}: {1} + Błąd podczas wywoływania metody czyszczącej testu dla klasy testowej {0}: {1} + + + + TestCleanup Stack Trace + Ślad stosu dla TestCleanup + + Data source '{0}' cannot be found in the test configuration settings W ustawieniach konfiguracji testu nie można znaleźć źródła danych „{0}” + + --- End of inner exception stack trace --- + --- Koniec śledzenia stosu wyjątku wewnętrznego --- + + The unit test adapter failed to connect to the data source or to read the data. For more information on troubleshooting this error, see "Troubleshooting Data-Driven Unit Tests" (http://go.microsoft.com/fwlink/?LinkId=62412) in the MSDN Library. Error details: {0} Adapter testu jednostkowego nie mógł połączyć się ze źródłem danych lub odczytać danych. Aby uzyskać więcej informacji o rozwiązywaniu tego błędu, zobacz „Rozwiązywanie problemów z testami jednostkowymi sterowanymi danymi” (http://go.microsoft.com/fwlink/?LinkId=62412) w bibliotece MSDN. Szczegóły błędu: {0} + + UTA031: class {0} does not have valid TestContext property. TestContext must be of type TestContext, must be non-static, and must be public. For example: public TestContext TestContext. + UTA031: Klasa {0}nie ma prawidłowej właściwości TestContext. Element TestContext musi być typu TestContext, musi być niestatyczny i musi być publiczny. Na przykład : public TestContext TestContext. + + + + UTA007: Method {1} defined in class {0} does not have correct signature. Test method marked with the [TestMethod] attribute must be non-static, public, return-type as void and should not take any parameter. Example: public void Test.Class1.Test(). Additionally, if you are using async-await in test method then return-type must be 'Task' or 'ValueTask'. Example: public async Task Test.Class1.Test2() + UTA007: metoda {1} zdefiniowana w klasie {0}nie ma poprawnej sygnatury. Metoda testowa oznaczona przez atrybut [TestMethod] musi być niestatyczna, publiczna, zwracać wartość typu void i nie powinna przyjmować żadnego parametru. Przykład: public void Test.Class1.Test(). Ponadto, jeśli używasz oczekiwanie asynchroniczne w metodzie testowej, wtedy zwracanym typem musi być „Task” lub „ValueTask”. Przykład: public async Task Test.Class1.Test2() + + + + UTA054: {0}.{1} has invalid Timeout attribute. The timeout must be an integer value greater than 0. + UTA054: {0}.{1} ma nieprawidłowy atrybut Timeout. Limit czasu musi być liczbą całkowitą większą niż 0. + + + + UTA014: {0}: Cannot define more than one method with the AssemblyCleanup attribute inside an assembly. + UTA014: {0}: W zestawie nie można zdefiniować więcej niż jednej metody z atrybutem AssemblyCleanup. + + + + UTA013: {0}: Cannot define more than one method with the AssemblyInitialize attribute inside an assembly. + UTA013: {0}: W zestawie nie można zdefiniować więcej niż jednej metody z atrybutem AssemblyInitialize. + + + + UTA026: {0}: Cannot define more than one method with the ClassCleanup attribute inside a class. + UTA026: {0}: W klasie nie można zdefiniować więcej niż jednej metody z atrybutem ClassCleanup. + + + + UTA025: {0}: Cannot define more than one method with the ClassInitialize attribute inside a class. + UTA025: {0}: W klasie nie można zdefiniować więcej niż jednej metody z atrybutem ClassInitialize. + + + + UTA024: {0}: Cannot define more than one method with the TestCleanup attribute. + UTA024: {0}: Nie można zdefiniować więcej niż jednej metody z atrybutem TestCleanup. + + + + UTA018: {0}: Cannot define more than one method with the TestInitialize attribute. + UTA018: {0}: Nie można zdefiniować więcej niż jednej metody z atrybutem TestInitialize. + + + + UTA001: TestClass attribute defined on non-public class {0} + UTA001: Atrybut TestClass zdefiniowany dla niepublicznej klasy {0} + + + + UTA023: {0}: Cannot define predefined property {2} on method {1}. + UTA023: {0}: Nie można zdefiniować wstępnie zdefiniowanej właściwości {2} dla metody {1}. + + + + TestClass attribute defined on generic non-abstract class {0} + Atrybut TestClass zdefiniowany w ogólnej klasie nieabstrakcyjnej {0} + + + + UTA021: {0}: Null or empty custom property defined on method {1}. The custom property must have a valid name. + UTA021: {0}: Zerowa lub pusta niestandardowa właściwość została zdefiniowana dla metody {1}. Niestandardowa właściwość musi mieć prawidłową nazwę. + + + + An unhandled exception was thrown by the 'Execute' method. Please report this error to the author of the attribute '{0}'. +{1} + Metoda „Execute” zgłosiła nieobsługiwany wyjątek. Zgłoś ten błąd autorowi atrybutu „{0}”. +{1} + + + + The ExpectedException attribute defined on test method {0}.{1} threw an exception during construction. +{2} + Atrybut ExpectedException zdefiniowany dla metody testowej {0}.{1} zgłosił wyjątek w trakcie konstruowania. +{2} + + + + Failed to obtain the exception thrown by test method {0}.{1}. + Nie powiodło się uzyskanie wyjątku zgłoszonego przez metodę testową {0}.{1}. + + + + Initialization method {0}.{1} threw exception. {2}. + Metoda inicjowania {0}.{1} zgłosiła wyjątek. {2}. + + + + Unable to create instance of class {0}. Error: {1}. + Nie można utworzyć wystąpienia klasy {0}. Błąd: {1}. + + + + Method {0}.{1} does not exist. + Metoda {0}.{1} nie istnieje. + + + + The test method '{0}.{1}' has multiple attributes derived from '{2}' defined on it. Only one such attribute is allowed. + Metoda testowa "{0}.{1}” ma zdefiniowanych wiele atrybutów pochodzących z „{2}”. Dozwolony jest tylko jeden taki atrybut. + + + + Error in executing test. No result returned by extension. If using extension of TestMethodAttribute then please contact vendor. + Błąd podczas wykonywania testu. Rozszerzenie nie zwróciło żadnego wyniku. W przypadku korzystania z rozszerzenia atrybutu TestMethodAttribute należy skontaktować się z dostawcą. + + + + Cannot find a valid constructor for test class '{0}'. Valid constructors are 'public' and either parameterless or with one parameter of type 'TestContext'. + Nie można odnaleźć prawidłowego konstruktora dla klasy testowej „{0}”. Prawidłowe konstruktory są „publiczne” i albo bez parametrów, albo z jednym parametrem typu „TestContext”. + + + + Unable to find property {0}.TestContext. Error:{1}. + Nie można znaleźć właściwości {0}.TestContext. Błąd:{1}. + + + + Unable to set TestContext property for the class {0}. Error: {1}. + Nie można ustawić właściwości TestContext w klasie {0}. Błąd: {1}. + + + + The {0}.TestContext has incorrect type. + Element {0}.TestContext ma niepoprawny typ. + + + + Method {0}.{1} has wrong signature. The method must be non-static, public, does not return a value and should not take any parameter. Additionally, if you are using async-await in method then return-type must be 'Task' or 'ValueTask'. + Metoda {0}.{1}ma nieprawidłową sygnaturę. Metoda musi być niestatyczna, publiczna, nie może zwracać wartości i nie powinna przyjmować żadnego parametru. Ponadto jeśli w metodzie jest używane oczekiwanie asynchroniczne, wtedy zwracanym typem musi być typ „Task” lub „ValueTask”. + + + + Test method {0}.{1} threw exception: +{2} + Metoda testowa {0}.{1} zgłosiła wyjątek: +{2} + + + + Unable to get type {0}. Error: {1}. + Nie można uzyskać typu {0}. Błąd: {1}. + + + + The called code threw an exception that was caught, but the exception value was null + Wywołany kod zgłosił wyjątek, który został przechwycony, ale wartość wyjątku miała wartość null + + + + {0} For UWP projects, if you are using UI objects in test consider using [UITestMethod] attribute instead of [TestMethod] to execute test in UI thread. + {0} Jeśli w projektach UWP korzystasz z obiektów interfejsu użytkownika podczas testowania, rozważ użycie atrybutu [UITestMethod] zamiast atrybutu [TestMethod], aby wykonywać test w wątku interfejsu użytkownika. + + + + (Failed to get the message for an exception of type {0} due to an exception.) + (Nie można pobrać komunikatu dotyczącego wyjątku typu {0} z powodu wyjątku). + + + + 'MSTest.TestAdapter' and 'MSTest.TestFramework' must have the same version. Found 'MSTest.TestAdapter' version '{0}' and 'MSTest.TestFramework' version '{1}'. Please make sure that the versions of 'MSTest.TestAdapter' and 'MSTest.TestFramework' NuGet packages have the same version. + Elementy „MSTest.TestAdapter” i „MSTest.TestFramework” muszą mieć tę samą wersję. Znaleziono element „MSTest.TestAdapter” w wersji „{0}” i element „MSTest.TestFramework” w wersji „{1}”. Upewnij się, że wersje pakietów NuGet „MSTest.TestAdapter” i „MSTest.TestFramework” mają tę samą wersję. + + Wrong number of objects for permutation. Should be greater than zero. Nieprawidłowa liczba obiektów permutacji. Ta liczba powinna być większa od zera. diff --git a/src/Adapter/MSTestAdapter.PlatformServices/Resources/xlf/Resource.pt-BR.xlf b/src/Adapter/MSTestAdapter.PlatformServices/Resources/xlf/Resource.pt-BR.xlf index 613c27c07d..775566e3b6 100644 --- a/src/Adapter/MSTestAdapter.PlatformServices/Resources/xlf/Resource.pt-BR.xlf +++ b/src/Adapter/MSTestAdapter.PlatformServices/Resources/xlf/Resource.pt-BR.xlf @@ -2,16 +2,116 @@ + + 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 + + + + Assembly cleanup method '{0}.{1}' was canceled + O método de limpeza do assembly "{0}.{1}" foi cancelado + + + + 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 + + + + Assembly initialize method '{0}.{1}' was canceled + O método de inicialização do assembly "{0}.{1}" foi cancelado + + + + MSTestAdapterV2 + MSTestAdapterV2 + + + + Exception occurred while enumerating IDataSource attribute on "{0}.{1}": {2} + Ocorreu uma exceção ao enumerar o atributo IDataSource em "{0}.{1}": {2} + {0}: TypeName with namespace, +{1}: Method name, +{2}: Exception details + + + Exception occurred while expanding IDataSource rows from attribute on "{0}.{1}": {2} + Ocorreu uma exceção ao expandir as linhas IDataSource do atributo em "{0}.{1}": {2} + {0}: TypeName with namespace, +{1}: Method name, +{2}: CannotExpandIDataSourceAttribute_DuplicateDisplayName or CannotExpandIDataSourceAttribute_CannotSerialize + + + Data on index {0} for "{1}" cannot be serialized. All data provided through "IDataSource" should be serializable. If you need to test non-serializable data sources, please make sure you add "TestDataSourceDiscovery" attribute on your test assembly and set the discovery option to "DuringExecution". + Os dados no índice {0} para "{1}" não podem ser serializados. Todos os dados fornecidos por meio de "IDataSource" devem ser serializáveis. Se você precisar testar fontes de dados não serializáveis, certifique-se de adicionar o atributo "TestDataSourceDiscovery" em seu assembly de teste e defina a opção de descoberta como "DuringExecution". + {0}: Zero based index if an element inside of an array, +{1}: Test name + + + Display name "{2}" on indexes {0} and {1} are duplicate. Display names should be unique. + O nome de exibição "{2}" nos índices {0} e {1} são duplicados. Os nomes de exibição devem ser exclusivos. + {0}, {1}: Zero based index if an element inside of an array +{2}: Test display name. + Could not find file '{0}'. Não foi possível encontrar o arquivo '{0}'. + + Cannot run test method '{0}.{1}': Test data doesn't match method parameters. Either the count or types are different. +Test expected {2} parameter(s), with types '{3}', +but received {4} argument(s), with types '{5}'. + Não é possível executar o método de teste '{0}.{1}': Os dados de teste não correspondem aos parâmetros do método. A contagem ou os tipos são diferentes. +Testar {2} parâmetros esperados, com tipos '{3}', +mas {4} argumentos recebidos, com tipos '{5}'. + + + + Cannot run test method '{0}.{1}': Method has parameters, but does not define any test source. Use '[DataRow]', '[DynamicData]', or a custom 'ITestDataSource' data source to provide test data. + Não é possível executar o método de teste '{0}.{1}': o método tem parâmetros, mas não define nenhuma fonte de teste. Use '[DataRow]', '[DynamicData]' ou uma fonte de dados 'ITestDataSource' personalizada para fornecer dados de teste. + + + + 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 + + + + Class cleanup method '{0}.{1}' was canceled + O método de limpeza de classe "{0}.{1}" foi cancelado + + + + 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 + + + + Class initialize method '{0}.{1}' was canceled + O método de inicialização de classe "{0}.{1}" foi cancelado + + The parameter should not be null or empty. O parâmetro não deve ser nulo ou vazio. + + MSTestAdapter failed to discover tests in class '{0}' of assembly '{1}' because {2}. + O MSTestAdapter não conseguiu descobrir testes na classe '{0}' do assembly '{1}' devido a {2}. + + + + {0} (Data Row {1}) + {0} (Linha de Dados {1}) + + + + Debug Trace: + Rastreamento de Depuração: + + Test Run deployment issue: Bad deployment item: '{0}': output directory '{1}' specifies the item to be deployed outside deployment root directory which is not allowed. Problema de implantação de Execução de Teste: item de implantação inválido: '{0}': o diretório de saída '{1}' especifica o item a ser implantado fora do diretório raiz de implantação, o que não é permitido. @@ -77,16 +177,108 @@ item de implantação '{0}' (diretório de saída '{1}') + + [MSTest][Discovery][{0}] {1} + [MSTest][Descoberta][{0}] {1} + + + + Both '.runsettings' and '.testconfig.json' files have been detected. Please select only one of these test configuration files. + Ambos os arquivos '.runsettings' e '.testconfig.json' foram detectados. Selecione apenas um desses arquivos de configuração de teste. + + + + {0}: {1} + {0}: {1} + + + + "{0}": (Failed to get exception description due to an exception of type "{1}". + "{0}": (Falha ao obter a descrição da exceção devido a uma exceção do tipo "{1}". + {0}: Type of the original exception that we're trying to get the description of. +{1}: Thrown exception + + + Exceptions thrown: + Exceções lançadas: + This is usually precedes by TestAssembly_AssemblyDiscoveryFailure message, and preceded by list of exceptions thrown in a test discovery session. + + + Test '{0}' was canceled + O teste "{0}" foi cancelado + + + + Test '{0}' timed out after {1}ms + O teste "{0}" atingiu o tempo limite após {1}ms + + + + Getting custom attributes for type {0} threw exception (will ignore and use the reflection way): {1} + A obtenção de atributos personalizados para o tipo {0} gerou uma exceção (irá ignorar e usar o modo de reflexão): {1} + {0}: Attribute full type name. +{1}: Exception description + + + The type of the generic parameter '{0}' could not be inferred. + O tipo do parâmetro genérico "{0}" não pôde ser inferido. + + + + The generic test method '{0}' doesn't have arguments, so the generic parameter cannot be inferred. + O método de teste genérico "{0}" não tem argumentos; portanto, o parâmetro genérico não pode ser inferido. + + + + Found two conflicting types for generic parameter '{0}'. The conflicting types are '{1}' and '{2}'. + Foram encontrados dois tipos conflitantes para o parâmetro genérico "{0}". Os tipos conflitantes são "{1}" e "{2}". + + + + Invalid value '{0}' specified for 'ClassCleanupLifecycle'. Supported scopes are {1}. + Valor inválido '{0}' especificado para 'ClassCleanupLifecycle'. Os escopos suportados são {1}. + {Locked="ClassCleanupLifecycle"} + + + Invalid value '{0}' specified for 'Scope'. Supported scopes are {1}. + Valor inválido '{0}' especificado para 'Scope'. Os escopos compatíveis são {1}. + {Locked="Scope"} + + + Invalid value '{0}' specified for 'Workers'. The value should be a non-negative integer. + Valor inválido '{0}' especificado para 'Workers'. O valor deve ser um inteiro não negativo. + {Locked="Workers"} + + + Invalid settings '{0}'. Unexpected XmlAttribute: '{1}'. + Configurações inválidas '{0}'. XmlAttribute inesperado: '{1}'. + + MSTestAdapter encountered an unexpected element '{0}' in its settings '{1}'. Remove this element and try again. MSTestAdapter encontrou um elemento inesperado '{0}' em suas configurações '{1}'. Remova este elemento e tente novamente. + + Invalid settings '{0}'. Unexpected XmlElement: '{1}'. + Configurações inválidas '{0}'. XmlElement inesperado: '{1}'. + + Invalid value '{0}' for runsettings entry '{1}', setting will be ignored. Valor inválido "{0}" para a entrada runsettings "{1}", a configuração será ignorada. + + Invalid value '{0}' for runsettings entry '{1}', setting will be ignored. + Valor inválido "{0}" para a entrada runsettings "{1}", a configuração será ignorada. + + + + Warning : A testsettings file or a vsmdi file is not supported with the MSTest V2 Adapter. + Aviso: um arquivo testsettings ou um arquivo vsmdi não tem suporte no MSTest V2 Adapter. + + Test Run deployment issue: The assembly or module '{0}' was not found. Reason: {1} Problema de implantação de Execução de Teste: o assembly ou módulo '{0}' não foi encontrado. Motivo: {1} @@ -97,21 +289,309 @@ Problema de implantação de Execução de Teste: o assembly ou módulo '{0}' não foi encontrado. + + An older version of MSTestV2 package is loaded in assembly, test discovery might fail to discover all data tests if they depend on `.runsettings` file. + Uma versão mais antiga do pacote MSTestV2 é carregada no assembly, a descoberta de teste pode falhar ao descobrir todos os testes de dados se eles dependerem do arquivo `.runsettings`. + + + + Runsettings entry '<ExecutionApartmentState>STA</ExecutionApartmentState>' is not supported on non-Windows OSes. + Não há suporte para a entrada runsettings "<ExecutionApartmentState>STA</ExecutionApartmentState>" em sistemas operacionais que não sejam Windows. + + + + Running tests in any of the provided sources is not supported for the selected platform + Não há suporte para execução de teste em qualquer uma das origens fontes fornecidas para a plataforma selecionada + + + + Failed to discover tests from assembly {0}. Reason:{1} + Falha ao descobrir testes por meio do assembly {0}. Motivo: {1} + + + + File does not exist: {0} + O arquivo não existe: {0} + + + + 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 + + + + Test cleanup method '{0}.{1}' was canceled + O método de limpeza de teste "{0}.{1}" foi cancelado + + + + TestContext cannot be Null. + TestContext não pode ser Nulo. + + + + TestContext Messages: + Mensagens TestContext: + + + + 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 + + + + Test initialize method '{0}.{1}' was canceled + O método de inicialização de teste "{0}.{1}" foi cancelado + + + + Test method {0} was not found. + O método de teste {0} não foi encontrado. + + + + Test Parallelization enabled for {0} (Workers: {1}, Scope: {2}) + Paralelização de Teste habilitada para {0} (Workers: {1}, Scope: {2}) + {Locked="Workers"}{Locked="Scope"} + {0}_{1} {2} {0}_{1} {2} + + Unable to load types from the test source '{0}'. Some or all of the tests in this source may not be discovered. +Error: {1} + Não é possível carregar tipos da fonte de teste '{0}'. Alguns ou todos os testes nessa fonte podem não ser descobertos. +Erro: {1} + + + + Assembly Cleanup method {0}.{1} failed. Error Message: {2}. StackTrace: {3} + Falha no método de Limpeza de Assembly {0}.{1}. Mensagem de Erro: {2}. StackTrace: {3} + + + + Assembly Initialization method {0}.{1} threw exception. {2}: {3}. Aborting test execution. + O método de Inicialização de Assembly {0}.{1} lançou uma exceção. {2}: {3}. Anulando execução de teste. + + + + Class Cleanup method {0}.{1} failed. Error Message: {2}. Stack Trace: {3} + Falha no método de Limpeza de Classe {0}.{1}. Mensagem de Erro: {2}. Rastreamento de Pilha: {3} + + + + Class Initialization method {0}.{1} threw exception. {2}: {3}. + O método de Inicialização de Classe {0}.{1} lançou uma exceção. {2}: {3}. + + + + Method {0}.{1} has wrong signature. The method must be static, public, does not return a value and should not take any parameter. Additionally, if you are using async-await in method then return-type must be 'Task' or 'ValueTask'. + O método {0}.{1} tem a assinatura incorreta. O método deve ser estático, público, não deve retornar um valor nem receber parâmetro. Além disso, se você estiver usando async-await no método, o return-type deverá ser "Task" ou "ValueTask". + + + + Method {0}.{1} has wrong signature. The method must be static, public, does not return a value and should take a single parameter of type TestContext. Additionally, if you are using async-await in method then return-type must be 'Task' or 'ValueTask'. + O método {0}.{1} tem a assinatura incorreta. O método deve ser estático, público, não retornar um valor e deve usar um único parâmetro do tipo TestContext. Além disso, se você estiver usando async-await no método, o return-type deverá ser "Task" ou "ValueTask". + + + + TestCleanup method {0}.{1} threw exception. {2}. + O método TestCleanup {0}.{1} gerou a exceção. {2}. + + + + Error calling Test Cleanup method for test class {0}: {1} + Erro ao chamar o método Test Cleanup para a classe de teste {0}: {1} + + + + TestCleanup Stack Trace + Rastreamento de pilha TestCleanup + + Data source '{0}' cannot be found in the test configuration settings A fonte de dados '{0}' não pode ser encontrada nas configurações de teste + + --- End of inner exception stack trace --- + --- Fim do rastreamento de pilha de exceção interna --- + + The unit test adapter failed to connect to the data source or to read the data. For more information on troubleshooting this error, see "Troubleshooting Data-Driven Unit Tests" (http://go.microsoft.com/fwlink/?LinkId=62412) in the MSDN Library. Error details: {0} O adaptador de teste de unidade falhou ao conectar-se à fonte de dados ou ao ler os dados. Para obter mais informações sobre como solucionar esse erro, consulte "Solucionando Problemas em Testes de Unidade Controlados por Dados" (http://go.microsoft.com/fwlink/?LinkId=62412) na Biblioteca do MSDN. Detalhes do erro: {0} + + UTA031: class {0} does not have valid TestContext property. TestContext must be of type TestContext, must be non-static, and must be public. For example: public TestContext TestContext. + UTA031: a classe {0} não tem uma propriedade TestContext válida. TestContext deve ser do tipo TestContext, não deve ser estático e deve ser público. Por exemplo: public TestContext TestContext. + + + + UTA007: Method {1} defined in class {0} does not have correct signature. Test method marked with the [TestMethod] attribute must be non-static, public, return-type as void and should not take any parameter. Example: public void Test.Class1.Test(). Additionally, if you are using async-await in test method then return-type must be 'Task' or 'ValueTask'. Example: public async Task Test.Class1.Test2() + UTA007: o método {1} definido na classe {0} não tem a assinatura correta. O método de teste marcado com o atributo [TestMethod] deve ser não estático, público, com tipo de retorno nulo e não deve receber parâmetros. Exemplo: Test.Class1.Test() público nulo. Além disso, se você estiver usando async-await no método de teste, return-type deverá ser "Task" ou "ValueTask". Exemplo: public async Task Test.Class1.Test2() + + + + UTA054: {0}.{1} has invalid Timeout attribute. The timeout must be an integer value greater than 0. + UTA054: {0}.{1} tem atributo Timeout inválido. O tempo limite deve ser um valor inteiro maior que 0. + + + + UTA014: {0}: Cannot define more than one method with the AssemblyCleanup attribute inside an assembly. + UTA014: {0}: não é possível definir mais de um método com o atributo AssemblyCleanup em um assembly. + + + + UTA013: {0}: Cannot define more than one method with the AssemblyInitialize attribute inside an assembly. + UTA013: {0}: não é possível definir mais de um método com o atributo AssemblyInitialize dentro de um assembly. + + + + UTA026: {0}: Cannot define more than one method with the ClassCleanup attribute inside a class. + UTA026: {0}: não é possível definir mais de um método com o atributo ClassCleanup dentro de uma classe. + + + + UTA025: {0}: Cannot define more than one method with the ClassInitialize attribute inside a class. + UTA025: {0}: não é possível definir mais de um método com o atributo ClassInitialize em uma classe. + + + + UTA024: {0}: Cannot define more than one method with the TestCleanup attribute. + UTA024: {0}: não é possível definir mais de um método com o atributo TestCleanup. + + + + UTA018: {0}: Cannot define more than one method with the TestInitialize attribute. + UTA018: {0}: não é possível definir mais de um método com o atributo TestInitialize. + + + + UTA001: TestClass attribute defined on non-public class {0} + UTA001: atributo TestClass definido em classe não pública {0} + + + + UTA023: {0}: Cannot define predefined property {2} on method {1}. + UTA023: {0}: não é possível definir a propriedade predefinida {2} no método {1}. + + + + TestClass attribute defined on generic non-abstract class {0} + Atributo TestClass definido em uma classe genérica não abstrata {0} + + + + UTA021: {0}: Null or empty custom property defined on method {1}. The custom property must have a valid name. + UTA021: {0}: Propriedade personalizada nula ou vazia definida no método {1}. A propriedade personalizada deve ter um nome válido. + + + + An unhandled exception was thrown by the 'Execute' method. Please report this error to the author of the attribute '{0}'. +{1} + Uma exceção sem tratamento foi lançada pelo método "Execute". Relate esse erro ao autor do atributo "{0}". +{1} + + + + The ExpectedException attribute defined on test method {0}.{1} threw an exception during construction. +{2} + O atributo ExpectedException definido no método de teste {0}.{1} emitiu uma exceção durante a construção. +{2} + + + + Failed to obtain the exception thrown by test method {0}.{1}. + Falha ao obter a exceção lançada pelo método de teste {0}.{1}. + + + + Initialization method {0}.{1} threw exception. {2}. + O método de inicialização {0}.{1} gerou exceção. {2}. + + + + Unable to create instance of class {0}. Error: {1}. + Não é possível criar instância da classe {0}. Erro: {1}. + + + + Method {0}.{1} does not exist. + O método {0}.{1} não existe. + + + + The test method '{0}.{1}' has multiple attributes derived from '{2}' defined on it. Only one such attribute is allowed. + O método de teste '{0}.{1}' tem várias características derivadas de '{2}' definidas nele. Apenas uma dessas características tem permissão. + + + + Error in executing test. No result returned by extension. If using extension of TestMethodAttribute then please contact vendor. + Erro ao executar o teste. Nenhum resultado retornado pela extensão. Se usar a extensão de TestMethodAttribute, entre em contato com o fornecedor. + + + + Cannot find a valid constructor for test class '{0}'. Valid constructors are 'public' and either parameterless or with one parameter of type 'TestContext'. + Não é possível localizar um construtor válido para a classe de teste '{0}'. Construtores válidos são 'public' e sem parâmetros ou com um parâmetro do tipo 'TestContext'. + + + + Unable to find property {0}.TestContext. Error:{1}. + Não é possível encontrar propriedade {0}.TestContext. Erro:{1}. + + + + Unable to set TestContext property for the class {0}. Error: {1}. + Não é definir a propriedade TestContext para a classe {0}. Erro: {1}. + + + + The {0}.TestContext has incorrect type. + O {0}.TestContext é do tipo incorreto. + + + + Method {0}.{1} has wrong signature. The method must be non-static, public, does not return a value and should not take any parameter. Additionally, if you are using async-await in method then return-type must be 'Task' or 'ValueTask'. + O método {0}.{1} tem a assinatura incorreta. O método deve ser não estático, público, não deve retornar um valor e não deve receber nenhum parâmetro. Além disso, se você estiver usando async-await no método, o return-type deverá ser "Task" ou "ValueTask". + + + + Test method {0}.{1} threw exception: +{2} + O método de teste {0}.{1} lançou a exceção: +{2} + + + + Unable to get type {0}. Error: {1}. + Não é possível obter o tipo {0}. Erro: {1}. + + + + The called code threw an exception that was caught, but the exception value was null + O código chamado lançou uma exceção que foi capturada, mas o valor da exceção era nulo + + + + {0} For UWP projects, if you are using UI objects in test consider using [UITestMethod] attribute instead of [TestMethod] to execute test in UI thread. + {0} Para projetos UWP, se você está usando objetos de IU no teste, considere usar o atributo [UITestMethod] em vez de [TestMethod] para executar o teste no thread da IU. + + + + (Failed to get the message for an exception of type {0} due to an exception.) + (Falha ao obter a mensagem para uma exceção do tipo {0} devido a uma exceção.) + + + + 'MSTest.TestAdapter' and 'MSTest.TestFramework' must have the same version. Found 'MSTest.TestAdapter' version '{0}' and 'MSTest.TestFramework' version '{1}'. Please make sure that the versions of 'MSTest.TestAdapter' and 'MSTest.TestFramework' NuGet packages have the same version. + 'MSTest.TestAdapter' e 'MSTest.TestFramework' devem ter a mesma versão. Encontrada a versão '{0}' do 'MSTest.TestAdapter' e a versão '{1}' do 'MSTest.TestFramework'. Por favor, verifique se as versões dos pacotes NuGet 'MSTest.TestAdapter' e 'MSTest.TestFramework' são iguais. + + Wrong number of objects for permutation. Should be greater than zero. Número incorreto de objetos para permutação. Deve ser maior que zero. diff --git a/src/Adapter/MSTestAdapter.PlatformServices/Resources/xlf/Resource.ru.xlf b/src/Adapter/MSTestAdapter.PlatformServices/Resources/xlf/Resource.ru.xlf index adadbea84e..5196050592 100644 --- a/src/Adapter/MSTestAdapter.PlatformServices/Resources/xlf/Resource.ru.xlf +++ b/src/Adapter/MSTestAdapter.PlatformServices/Resources/xlf/Resource.ru.xlf @@ -2,16 +2,116 @@ + + Assembly cleanup method '{0}.{1}' timed out after {2}ms + Время ожидания метода очистки сборки "{0}.{1}" истекло через {2} мс + + + + Assembly cleanup method '{0}.{1}' was canceled + Метод очистки сборки "{0}.{1}" отменен + + + + Assembly initialize method '{0}.{1}' timed out after {2}ms + Время ожидания метода инициализации сборки "{0}.{1}" истекло через {2} мс + + + + Assembly initialize method '{0}.{1}' was canceled + Метод инициализации сборки "{0}.{1}" отменен + + + + MSTestAdapterV2 + MSTestAdapterV2 + + + + Exception occurred while enumerating IDataSource attribute on "{0}.{1}": {2} + Возникло исключение при перечислении атрибута IDataSource в "{0}.{1}": {2} + {0}: TypeName with namespace, +{1}: Method name, +{2}: Exception details + + + Exception occurred while expanding IDataSource rows from attribute on "{0}.{1}": {2} + Возникло исключение при развертывании строк IDataSource из атрибута "{0}.{1}": {2} + {0}: TypeName with namespace, +{1}: Method name, +{2}: CannotExpandIDataSourceAttribute_DuplicateDisplayName or CannotExpandIDataSourceAttribute_CannotSerialize + + + Data on index {0} for "{1}" cannot be serialized. All data provided through "IDataSource" should be serializable. If you need to test non-serializable data sources, please make sure you add "TestDataSourceDiscovery" attribute on your test assembly and set the discovery option to "DuringExecution". + Не удается сериализовать данные в индексе {0} для "{1}". Все данные, предоставленные через IDataSource, должны быть сериализуемыми. Если необходимо протестировать несериализуемые источники данных, добавьте атрибут "TestDataSourceDiscovery" в тестовую сборку и установите для параметра обнаружения значение "DuringExecution". + {0}: Zero based index if an element inside of an array, +{1}: Test name + + + Display name "{2}" on indexes {0} and {1} are duplicate. Display names should be unique. + Отображаемые имена "{2}" в индексах {0} и {1} дублируются. Отображаемые имена должны быть уникальными. + {0}, {1}: Zero based index if an element inside of an array +{2}: Test display name. + Could not find file '{0}'. Не удалось найти файл "{0}". + + Cannot run test method '{0}.{1}': Test data doesn't match method parameters. Either the count or types are different. +Test expected {2} parameter(s), with types '{3}', +but received {4} argument(s), with types '{5}'. + Не удается запустить метод теста "{0}.{1}": тестовые данные не соответствуют параметрам метода. Не совпадают количество или типы. +Для теста ожидается следующее количество параметров: {2} типов "{3}", +но число полученных аргументов — {4} и они принадлежат к типам "{5}". + + + + Cannot run test method '{0}.{1}': Method has parameters, but does not define any test source. Use '[DataRow]', '[DynamicData]', or a custom 'ITestDataSource' data source to provide test data. + Не удается запустить метод теста "{0}.{1}": метод имеет параметры, но не определяет источник теста. Используйте "[DataRow]", "[DynamicData]" или настраиваемый источник данных "ITestDataSource" для предоставления тестовых данных. + + + + Class cleanup method '{0}.{1}' timed out after {2}ms + Время ожидания метода очистки класса "{0}.{1}" истекло через {2} мс + + + + Class cleanup method '{0}.{1}' was canceled + Метод очистки класса "{0}.{1}" отменен + + + + Class initialize method '{0}.{1}' timed out after {2}ms + Время ожидания метода инициализации класса "{0}.{1}" истекло через {2} мс + + + + Class initialize method '{0}.{1}' was canceled + Метод инициализации класса "{0}.{1}" отменен + + The parameter should not be null or empty. Этот параметр не должен быть пустым или иметь значение NULL. + + MSTestAdapter failed to discover tests in class '{0}' of assembly '{1}' because {2}. + Средству MSTestAdapter не удалось обнаружить тесты в классе "{0}" сборки "{1}", так как {2}. + + + + {0} (Data Row {1}) + {0} (строка данных {1}) + + + + Debug Trace: + Трассировка отладки: + + Test Run deployment issue: Bad deployment item: '{0}': output directory '{1}' specifies the item to be deployed outside deployment root directory which is not allowed. Ошибка развертывания при тестовом запуске: недопустимый элемент развертывания "{0}". В выходном каталоге "{1}" указано, что развертывание этого элемента должно быть выполнено не в корневом каталоге развертывания, что запрещено. @@ -77,16 +177,108 @@ элемент развертывания "{0}" (выходной каталог "{1}") + + [MSTest][Discovery][{0}] {1} + [MSTest][Discovery][{0}] {1} + + + + Both '.runsettings' and '.testconfig.json' files have been detected. Please select only one of these test configuration files. + Обнаружены одновременно файлы ".runsettings" и ".testconfig.json". Выберите только один из этих файлов тестовой конфигурации. + + + + {0}: {1} + {0}: {1} + + + + "{0}": (Failed to get exception description due to an exception of type "{1}". + "{0}": (Не удалось получить описание исключения из-за исключения типа "{1}". + {0}: Type of the original exception that we're trying to get the description of. +{1}: Thrown exception + + + Exceptions thrown: + Выданные исключения: + This is usually precedes by TestAssembly_AssemblyDiscoveryFailure message, and preceded by list of exceptions thrown in a test discovery session. + + + Test '{0}' was canceled + Тест "{0}" был отменен + + + + Test '{0}' timed out after {1}ms + Время ожидания теста "{0}" истекло через {1} мс + + + + Getting custom attributes for type {0} threw exception (will ignore and use the reflection way): {1} + При получении настраиваемых атрибутов для типа {0} возникло исключение (оно будет проигнорировано и будет использовано отражение): {1} + {0}: Attribute full type name. +{1}: Exception description + + + The type of the generic parameter '{0}' could not be inferred. + Не удалось определить тип универсального параметра "{0}". + + + + The generic test method '{0}' doesn't have arguments, so the generic parameter cannot be inferred. + Универсальный метод "{0}" не имеет аргументов, поэтому невозможно вывести универсальный параметр. + + + + Found two conflicting types for generic parameter '{0}'. The conflicting types are '{1}' and '{2}'. + Обнаружены два конфликтующих типа для универсального параметра "{0}". Конфликтующие типы: "{1}" и "{2}". + + + + Invalid value '{0}' specified for 'ClassCleanupLifecycle'. Supported scopes are {1}. + Для параметра "ClassCleanupLifecycle" указано недопустимое значение "{0}". Поддерживаемые области: {1}. + {Locked="ClassCleanupLifecycle"} + + + Invalid value '{0}' specified for 'Scope'. Supported scopes are {1}. + В поле "Scope" указано недопустимое значение "{0}". Поддерживаемые области: {1}. + {Locked="Scope"} + + + Invalid value '{0}' specified for 'Workers'. The value should be a non-negative integer. + В поле "Workers" указано недопустимое значение "{0}". Оно должно быть неотрицательным целым числом. + {Locked="Workers"} + + + Invalid settings '{0}'. Unexpected XmlAttribute: '{1}'. + Недопустимые параметры "{0}". Непредвиденный атрибут XmlAttribute: "{1}". + + MSTestAdapter encountered an unexpected element '{0}' in its settings '{1}'. Remove this element and try again. MSTestAdapter обнаружил непредвиденный элемент "{0}" в параметрах "{1}". Удалите этот элемент и повторите попытку. + + Invalid settings '{0}'. Unexpected XmlElement: '{1}'. + Недопустимые параметры "{0}". Непредвиденный элемент XmlElement: "{1}". + + Invalid value '{0}' for runsettings entry '{1}', setting will be ignored. Недопустимое значение "{0}" для записи runsettings "{1}", параметр будет пропущен. + + Invalid value '{0}' for runsettings entry '{1}', setting will be ignored. + Недопустимое значение "{0}" для записи runsettings "{1}", параметр будет пропущен. + + + + Warning : A testsettings file or a vsmdi file is not supported with the MSTest V2 Adapter. + Внимание! Адаптер MSTest версии 2 не поддерживает файл TESTSETTINGS или VSMDI. + + Test Run deployment issue: The assembly or module '{0}' was not found. Reason: {1} Ошибка развертывания тестового запуска: не удалось найти сборку или модуль "{0}". Причина: {1} @@ -97,21 +289,309 @@ Ошибка развертывания тестового запуска: не удалось найти сборку или модуль "{0}". + + An older version of MSTestV2 package is loaded in assembly, test discovery might fail to discover all data tests if they depend on `.runsettings` file. + В сборку загружена старая версия пакета MSTestV2. При обнаружении тестов могут быть обнаружены не все тесты данных, если они зависят от файла ".runsettings". + + + + Runsettings entry '<ExecutionApartmentState>STA</ExecutionApartmentState>' is not supported on non-Windows OSes. + Запись Runsettings "<ExecutionApartmentState>STA</ExecutionApartmentState>" не поддерживается в ОС, отличных от Windows. + + + + Running tests in any of the provided sources is not supported for the selected platform + Запуск тестов в любом из предоставленных источников не поддерживается на выбранной платформе + + + + Failed to discover tests from assembly {0}. Reason:{1} + Не удалось обнаружить тесты из сборки {0}. Причина:{1} + + + + File does not exist: {0} + Файл не существует: {0} + + + + Test cleanup method '{0}.{1}' timed out after {2}ms + Время ожидания метода очистки теста "{0}.{1}" истекло через {2} мс + + + + Test cleanup method '{0}.{1}' was canceled + Метод очистки теста "{0}.{1}" отменен + + + + TestContext cannot be Null. + TestContext не может иметь значение NULL. + + + + TestContext Messages: + Сообщения TestContext: + + + + Test initialize method '{0}.{1}' timed out after {2}ms + Время ожидания метода инициализации теста "{0}.{1}" истекло через {2} мс + + + + Test initialize method '{0}.{1}' was canceled + Метод инициализации теста "{0}.{1}" отменен + + + + Test method {0} was not found. + Метод теста {0} не найден. + + + + Test Parallelization enabled for {0} (Workers: {1}, Scope: {2}) + Включена параллелизация тестов для {0} (Workers: {1}, Scope: {2}) + {Locked="Workers"}{Locked="Scope"} + {0}_{1} {2} {0}_{1} {2} + + Unable to load types from the test source '{0}'. Some or all of the tests in this source may not be discovered. +Error: {1} + Не удалось загрузить типы из тестового источника "{0}". В этом источнике могут быть не обнаружены некоторые или все тесты. +Ошибка: {1} + + + + Assembly Cleanup method {0}.{1} failed. Error Message: {2}. StackTrace: {3} + Не удалось применить метод очистки сборки {0}.{1}. Сообщение об ошибке: {2}. Трассировка стека (StackTrace): {3}. + + + + Assembly Initialization method {0}.{1} threw exception. {2}: {3}. Aborting test execution. + Методом инициализации сборки {0}.{1} создано исключение. {2}: {3}. Выполнение теста прекращается. + + + + Class Cleanup method {0}.{1} failed. Error Message: {2}. Stack Trace: {3} + Не удалось применить метод очистки класса {0}.{1}. Сообщение об ошибке: {2}. Трассировка стека: {3}. + + + + Class Initialization method {0}.{1} threw exception. {2}: {3}. + Методом инициализации класса {0}.{1} создано исключение. {2}: {3}. + + + + Method {0}.{1} has wrong signature. The method must be static, public, does not return a value and should not take any parameter. Additionally, if you are using async-await in method then return-type must be 'Task' or 'ValueTask'. + Метод {0}.{1} имеет неправильную сигнатуру. Метод должен быть статическим и открытым, не должен возвращать значение и принимать параметры. Кроме того, при использовании async-await в методе возвращаемое значение должно иметь тип "Task" или "ValueTask". + + + + Method {0}.{1} has wrong signature. The method must be static, public, does not return a value and should take a single parameter of type TestContext. Additionally, if you are using async-await in method then return-type must be 'Task' or 'ValueTask'. + Метод {0}.{1} имеет неправильную сигнатуру. Метод должен быть статическим и открытым, не должен возвращать значение и должен принимать один параметр, имеющий тип TestContext. Кроме того, при использовании async-await в методе возвращаемое значение должно иметь тип "Task" или "ValueTask". + + + + TestCleanup method {0}.{1} threw exception. {2}. + Метод TestCleanup {0}.{1} создал исключение. {2}. + + + + Error calling Test Cleanup method for test class {0}: {1} + Ошибка при вызове метода TestCleanup для тестового класса {0}: {1} + + + + TestCleanup Stack Trace + Трассировка стека TestCleanup + + Data source '{0}' cannot be found in the test configuration settings Не удалось найти источник данных "{0}" в параметрах конфигурации теста + + --- End of inner exception stack trace --- + --- Конец трассировки стека внутренних исключений --- + + The unit test adapter failed to connect to the data source or to read the data. For more information on troubleshooting this error, see "Troubleshooting Data-Driven Unit Tests" (http://go.microsoft.com/fwlink/?LinkId=62412) in the MSDN Library. Error details: {0} Адаптеру модульных тестов не удалось подключиться к источнику данных, чтобы считать данные. Дополнительные сведения об устранении этой ошибки см. в разделе "Устранение неполадок в модульных тестах на основе данных" (http://go.microsoft.com/fwlink/?LinkId=62412) в библиотеке MSDN. Подробности об ошибке: {0} + + UTA031: class {0} does not have valid TestContext property. TestContext must be of type TestContext, must be non-static, and must be public. For example: public TestContext TestContext. + UTA031: в классе {0} отсутствует допустимое свойство TestContext. Свойство TestContext должно относиться к типу TestContext, быть нестатическим и открытым. Например: public TestContext TestContext. + + + + UTA007: Method {1} defined in class {0} does not have correct signature. Test method marked with the [TestMethod] attribute must be non-static, public, return-type as void and should not take any parameter. Example: public void Test.Class1.Test(). Additionally, if you are using async-await in test method then return-type must be 'Task' or 'ValueTask'. Example: public async Task Test.Class1.Test2() + UTA007: метод {1}, определенный в классе {0}, имеет неправильную сигнатуру. Метод теста, помеченный атрибутом [TestMethod], должен быть нестатическим, открытым и иметь тип возвращаемого значения void; он также не должен принимать параметры. Пример: public void Test.Class1.Test(). Кроме того, при использовании async-await в методе теста возвращаемое значение должно иметь тип "Task" или "ValueTask". Пример: public async Task Test.Class1.Test2() + + + + UTA054: {0}.{1} has invalid Timeout attribute. The timeout must be an integer value greater than 0. + UTA054: {0}. {1} имеет недопустимый атрибут времени ожидания. Значение времени ожидания должно быть положительным целым числом. + + + + UTA014: {0}: Cannot define more than one method with the AssemblyCleanup attribute inside an assembly. + UTA014: {0}: в сборке невозможно определить несколько методов с атрибутом AssemblyCleanup. + + + + UTA013: {0}: Cannot define more than one method with the AssemblyInitialize attribute inside an assembly. + UTA013: {0}: в сборке невозможно определить несколько методов с атрибутом AssemblyInitialize. + + + + UTA026: {0}: Cannot define more than one method with the ClassCleanup attribute inside a class. + UTA026: {0}: в классе невозможно определить несколько методов с атрибутом ClassCleanup. + + + + UTA025: {0}: Cannot define more than one method with the ClassInitialize attribute inside a class. + UTA025: {0}: в классе невозможно определить несколько методов с атрибутом ClassInitialize. + + + + UTA024: {0}: Cannot define more than one method with the TestCleanup attribute. + UTA024: {0}: невозможно определить несколько методов с атрибутом TestCleanup. + + + + UTA018: {0}: Cannot define more than one method with the TestInitialize attribute. + UTA018: {0}: невозможно определить несколько методов с атрибутом TestInitialize. + + + + UTA001: TestClass attribute defined on non-public class {0} + UTA001: атрибут TestClass определен в классе {0}, не являющемся открытым + + + + UTA023: {0}: Cannot define predefined property {2} on method {1}. + UTA023: {0}: не удается определить предопределенное свойство {2} в методе {1}. + + + + TestClass attribute defined on generic non-abstract class {0} + Атрибут TestClass определен в универсальном неабстрактном классе {0} + + + + UTA021: {0}: Null or empty custom property defined on method {1}. The custom property must have a valid name. + UTA021: {0}: в методе {1} определено пользовательское свойство, имя которого имеет значение NULL или пусто. Пользовательское свойство должно иметь допустимое имя. + + + + An unhandled exception was thrown by the 'Execute' method. Please report this error to the author of the attribute '{0}'. +{1} + Метод "Execute" сгенерировал необработанное исключение. Сообщите об этой ошибке автору атрибута "{0}". +{1} + + + + The ExpectedException attribute defined on test method {0}.{1} threw an exception during construction. +{2} + Атрибут ExpectedException, определенный в методе теста {0}.{1}, породил исключение во время создания. +{2} + + + + Failed to obtain the exception thrown by test method {0}.{1}. + Не удалось получить исключение, созданное методом теста {0}.{1}. + + + + Initialization method {0}.{1} threw exception. {2}. + Метод инициализации {0}.{1} вызвал исключение. {2}. + + + + Unable to create instance of class {0}. Error: {1}. + Не удалось создать экземпляр класса {0}. Ошибка: {1}. + + + + Method {0}.{1} does not exist. + Метод {0}.{1} не существует. + + + + The test method '{0}.{1}' has multiple attributes derived from '{2}' defined on it. Only one such attribute is allowed. + У метода тестирования "{0}.{1}" есть несколько атрибутов, производных от заданного в нем "{2}". Допускается только один такой атрибут. + + + + Error in executing test. No result returned by extension. If using extension of TestMethodAttribute then please contact vendor. + Ошибка при выполнении теста. Расширение не возвратило результаты. Если используется расширение атрибута TestMethodAttribute, обратитесь к поставщику. + + + + Cannot find a valid constructor for test class '{0}'. Valid constructors are 'public' and either parameterless or with one parameter of type 'TestContext'. + Не удается найти допустимый конструктор для тестового класса "{0}". Допустимые конструкторы должны быть определены как "public" и либо не иметь параметров, либо иметь один параметр типа "TestContext". + + + + Unable to find property {0}.TestContext. Error:{1}. + Не удается найти свойство {0}.TestContext. Ошибка: {1}. + + + + Unable to set TestContext property for the class {0}. Error: {1}. + Не удалось задать свойство TestContext для класса {0}. Ошибка: {1}. + + + + The {0}.TestContext has incorrect type. + Для свойства {0}.TestContext указан неправильный тип. + + + + Method {0}.{1} has wrong signature. The method must be non-static, public, does not return a value and should not take any parameter. Additionally, if you are using async-await in method then return-type must be 'Task' or 'ValueTask'. + Метод {0}.{1} имеет неправильную сигнатуру. Метод должен быть нестатическим и открытым, не должен возвращать значение и принимать параметры. Кроме того, при использовании async-await в методе возвращаемое значение должно иметь тип "Task" или "ValueTask". + + + + Test method {0}.{1} threw exception: +{2} + Метод теста {0}.{1} создал исключение: +{2}. + + + + Unable to get type {0}. Error: {1}. + Не удается получить тип {0}. Ошибка: {1}. + + + + The called code threw an exception that was caught, but the exception value was null + Вызванный код вызвал исключение, которое было перехвачено, но значение исключения было равно NULL + + + + {0} For UWP projects, if you are using UI objects in test consider using [UITestMethod] attribute instead of [TestMethod] to execute test in UI thread. + {0} В проектах UWP, если в тесте используются объекты пользовательского интерфейса, рассмотрите возможность использования атрибута [UITestMethod] вместо атрибута [TestMethod] для выполнения теста в потоке пользовательского интерфейса. + + + + (Failed to get the message for an exception of type {0} due to an exception.) + (Не удалось получить сообщение для исключения с типом {0} в связи с возникновением исключения.) + + + + 'MSTest.TestAdapter' and 'MSTest.TestFramework' must have the same version. Found 'MSTest.TestAdapter' version '{0}' and 'MSTest.TestFramework' version '{1}'. Please make sure that the versions of 'MSTest.TestAdapter' and 'MSTest.TestFramework' NuGet packages have the same version. + "MSTest.TestAdapter" и "MSTest.TestFramework" должны использовать одинаковую версию. Обнаружены "MSTest.TestAdapter" версии "{0}" и "MSTest.TestFramework" версии "{1}". Убедитесь, что версии пакетов NuGet "MSTest.TestAdapter" и "MSTest.TestFramework" совпадают. + + Wrong number of objects for permutation. Should be greater than zero. Неправильное число объектов для перестановки. Оно должно быть больше нуля. diff --git a/src/Adapter/MSTestAdapter.PlatformServices/Resources/xlf/Resource.tr.xlf b/src/Adapter/MSTestAdapter.PlatformServices/Resources/xlf/Resource.tr.xlf index 79d35b165a..1ba2e803d3 100644 --- a/src/Adapter/MSTestAdapter.PlatformServices/Resources/xlf/Resource.tr.xlf +++ b/src/Adapter/MSTestAdapter.PlatformServices/Resources/xlf/Resource.tr.xlf @@ -2,16 +2,116 @@ + + 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ı + + + + Assembly cleanup method '{0}.{1}' was canceled + '{0}.{1}' bütünleştirilmiş kod temizleme yöntemi iptal edildi + + + + 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ı + + + + Assembly initialize method '{0}.{1}' was canceled + '{0}.{1}' derleme başlatma yöntemi iptal edildi + + + + MSTestAdapterV2 + MSTestAdapterV2 + + + + Exception occurred while enumerating IDataSource attribute on "{0}.{1}": {2} + "{0}.{1} " üzerinde IDataSource özniteliği numaralandırılırken özel durum oluştu: {2} + {0}: TypeName with namespace, +{1}: Method name, +{2}: Exception details + + + Exception occurred while expanding IDataSource rows from attribute on "{0}.{1}": {2} + "{0}. {1}" üzerindeki öznitelikten IDataSource satırları genişletilirken özel durum oluştu: {2} + {0}: TypeName with namespace, +{1}: Method name, +{2}: CannotExpandIDataSourceAttribute_DuplicateDisplayName or CannotExpandIDataSourceAttribute_CannotSerialize + + + Data on index {0} for "{1}" cannot be serialized. All data provided through "IDataSource" should be serializable. If you need to test non-serializable data sources, please make sure you add "TestDataSourceDiscovery" attribute on your test assembly and set the discovery option to "DuringExecution". + "{1}" için {0} indeksindeki veriler serileştirilemez. "IDataSource" aracılığıyla sağlanan tüm veriler serileştirilebilir olmalıdır. Serileştirilemeyen veri kaynaklarını test etmeniz gerekiyorsa, lütfen test derlemenize "TestDataSourceDiscovery" özniteliğini eklediğinizden ve keşif seçeneğini "DuringExecution" olarak ayarladığınızdan emin olun. + {0}: Zero based index if an element inside of an array, +{1}: Test name + + + Display name "{2}" on indexes {0} and {1} are duplicate. Display names should be unique. + {0} ve {1}dizinlerinde görünen ad " {2}" yineleniyor. Görünen adlar benzersiz olmalıdır. + {0}, {1}: Zero based index if an element inside of an array +{2}: Test display name. + Could not find file '{0}'. '{0}' adlı dosya bulunamadı. + + Cannot run test method '{0}.{1}': Test data doesn't match method parameters. Either the count or types are different. +Test expected {2} parameter(s), with types '{3}', +but received {4} argument(s), with types '{5}'. + '{0}.{1}' test yöntemi çalıştırılamıyor: Test verileri yöntem parametreleriyle eşleşmiyor. Ya sayıları ya da türleri birbirinden farklı. +Test '{3}' türünde {2} parametre bekledi, +ancak, '{5}' türünde {4} bağımsız değişken aldı. + + + + Cannot run test method '{0}.{1}': Method has parameters, but does not define any test source. Use '[DataRow]', '[DynamicData]', or a custom 'ITestDataSource' data source to provide test data. + '{0}.{1}' test yöntemi çalıştırılamıyor: Yöntemin parametreleri var ancak herhangi bir test kaynağı tanımlamıyor. Test verilerini sağlamak için '[DataRow]', '[DynamicData]' veya özel bir 'ITestDataSource' veri kaynağı kullanın. + + + + 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ı + + + + Class cleanup method '{0}.{1}' was canceled + '{0}.{1}' sınıf temizleme yöntemi iptal edildi + + + + 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ı + + + + Class initialize method '{0}.{1}' was canceled + '{0}.{1}' sınıf başlatma yöntemi iptal edildi + + The parameter should not be null or empty. Parametre null veya boş olmamalıdır. + + MSTestAdapter failed to discover tests in class '{0}' of assembly '{1}' because {2}. + MSTestAdapter, '{1}' bütünleştirilmiş kodunun '{0}' sınıfındaki testleri bulamadı. Nedeni: {2}. + + + + {0} (Data Row {1}) + {0} (Veri Satırı {1}) + + + + Debug Trace: + Hata Ayıklama İzleyici: + + Test Run deployment issue: Bad deployment item: '{0}': output directory '{1}' specifies the item to be deployed outside deployment root directory which is not allowed. Test Çalıştırması dağıtım sorunu: '{0}' dağıtım öğesi hatalı: '{1}' çıktı dizininde öğenin dağıtım kök dizini dışında dağıtılması gerektiği belirtilmiş, ancak buna izin verilmiyor. @@ -77,16 +177,108 @@ '{0}' dağıtım öğesi (çıktı dizini '{1}') + + [MSTest][Discovery][{0}] {1} + [MSTest][Discovery][{0}] {1} + + + + Both '.runsettings' and '.testconfig.json' files have been detected. Please select only one of these test configuration files. + Hem '.runsettings' hem de '.testconfig.json' dosyaları algılandı. Lütfen bu test yapılandırma dosyalarından yalnızca birini seçin. + + + + {0}: {1} + {0}: {1} + + + + "{0}": (Failed to get exception description due to an exception of type "{1}". + "{0}": ("{1}" türündeki bir istisna nedeniyle özel durum açıklaması alınamadı. + {0}: Type of the original exception that we're trying to get the description of. +{1}: Thrown exception + + + Exceptions thrown: + Oluşturulan özel durumlar: + This is usually precedes by TestAssembly_AssemblyDiscoveryFailure message, and preceded by list of exceptions thrown in a test discovery session. + + + Test '{0}' was canceled + '{0}' testi iptal edildi + + + + Test '{0}' timed out after {1}ms + '{0}' testi {1} ms sonra zaman aşımına uğradı + + + + Getting custom attributes for type {0} threw exception (will ignore and use the reflection way): {1} + {0} tipi için özel niteliklerin alınması özel durum oluşturdu (yok sayar ve yansıma yolunu kullanır): {1} + {0}: Attribute full type name. +{1}: Exception description + + + The type of the generic parameter '{0}' could not be inferred. + '{0}' genel parametre türü çıkarsanamadı. + + + + The generic test method '{0}' doesn't have arguments, so the generic parameter cannot be inferred. + '{0}' genel test metodu bağımsız değişkenlere sahip olmadığından, genel parametre çıkarsanamıyor. + + + + Found two conflicting types for generic parameter '{0}'. The conflicting types are '{1}' and '{2}'. + '{0}' genel parametresi için iki çakışan tür bulundu. Çakışan türler '{1}' ile '{2}'. + + + + Invalid value '{0}' specified for 'ClassCleanupLifecycle'. Supported scopes are {1}. + 'ClassCleanupLifecycle' için geçersiz '{0}' değeri belirtildi. Desteklenen kapsamlar {1}'dir. + {Locked="ClassCleanupLifecycle"} + + + Invalid value '{0}' specified for 'Scope'. Supported scopes are {1}. + 'Scope' için geçersiz '{0}' değeri belirtildi. Desteklenen kapsamlar {1}. + {Locked="Scope"} + + + Invalid value '{0}' specified for 'Workers'. The value should be a non-negative integer. + 'Workers' için geçersiz '{0}' değeri belirtildi. Değer negatif olmayan bir tamsayı olmalıdır. + {Locked="Workers"} + + + Invalid settings '{0}'. Unexpected XmlAttribute: '{1}'. + Geçersiz '{0}' ayarları. Beklenmeyen XmlAttribute: '{1}'. + + MSTestAdapter encountered an unexpected element '{0}' in its settings '{1}'. Remove this element and try again. MSTestAdapter, '{1}' ayarlarında beklenmedik bir öğeyle ('{0}') karşılaştı. Bu öğeyi kaldırıp yeniden deneyin. + + Invalid settings '{0}'. Unexpected XmlElement: '{1}'. + Geçersiz '{0}' ayarları. Beklenmeyen XmlElement: '{1}'. + + Invalid value '{0}' for runsettings entry '{1}', setting will be ignored. '{1}' çalıştırma ayarları girişi için '{0}' değeri geçersiz, ayar yoksayılacak. + + Invalid value '{0}' for runsettings entry '{1}', setting will be ignored. + '{1}' çalıştırma ayarları girişi için '{0}' değeri geçersiz, ayar yoksayılacak. + + + + Warning : A testsettings file or a vsmdi file is not supported with the MSTest V2 Adapter. + Uyarı : MSTest V2 Adapter ile bir testsettings dosyası veya bir vsmdi dosyası desteklenmez. + + Test Run deployment issue: The assembly or module '{0}' was not found. Reason: {1} Test Çalıştırması dağıtım sorunu: '{0}' adlı bütünleştirilmiş kod veya modül bulunamadı. Nedeni: {1} @@ -97,21 +289,309 @@ Test Çalıştırması dağıtım sorunu: '{0}' adlı bütünleştirilmiş kod veya modül bulunamadı. + + An older version of MSTestV2 package is loaded in assembly, test discovery might fail to discover all data tests if they depend on `.runsettings` file. + Montaja MSTestV2 paketinin daha eski bir sürümü yüklenir, test keşfi, `.runsettings` dosyasına bağlılarsa tüm veri testlerini keşfetmede başarısız olabilir. + + + + Runsettings entry '<ExecutionApartmentState>STA</ExecutionApartmentState>' is not supported on non-Windows OSes. + '<ExecutionApartmentState>STA</ExecutionApartmentState>' çalışma ayarları girişi Windows dışı işletim sistemlerinde desteklenmiyor. + + + + Running tests in any of the provided sources is not supported for the selected platform + Testlerin sağlanan kaynakların herhangi birinde çalıştırılması seçili platformda desteklenmiyor + + + + Failed to discover tests from assembly {0}. Reason:{1} + {0} bütünleştirilmiş kodundan testler bulunamadı. Neden:{1} + + + + File does not exist: {0} + Dosya yok: {0} + + + + 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ı + + + + Test cleanup method '{0}.{1}' was canceled + '{0}.{1}' test temizleme yöntemi iptal edildi + + + + TestContext cannot be Null. + TestContext, Null olamaz. + + + + TestContext Messages: + TestContext İletileri: + + + + 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ı + + + + Test initialize method '{0}.{1}' was canceled + '{0}.{1}' test başlatma yöntemi iptal edildi + + + + Test method {0} was not found. + {0} test metodu bulunamadı. + + + + Test Parallelization enabled for {0} (Workers: {1}, Scope: {2}) + {0} için Test Paralelleştirme etkin (Workers: {1}, Scope: {2}) + {Locked="Workers"}{Locked="Scope"} + {0}_{1} {2} {0}_{1} {2} + + Unable to load types from the test source '{0}'. Some or all of the tests in this source may not be discovered. +Error: {1} + '{0}' test kaynağından türler yüklenemiyor. Kaynaktaki testlerin bazıları veya tümü bulunamayabilir. +Hata: {1} + + + + Assembly Cleanup method {0}.{1} failed. Error Message: {2}. StackTrace: {3} + Bütünleştirilmiş Kod Temizleme metodu ({0}.{1}) başarısız oldu. Hata İletisi: {2}. StackTrace: {3} + + + + Assembly Initialization method {0}.{1} threw exception. {2}: {3}. Aborting test execution. + Bütünleştirilmiş Kod Başlatma metodu ({0}.{1}) özel durum oluşturdu. {2}: {3}. Test yürütmesi durduruluyor. + + + + Class Cleanup method {0}.{1} failed. Error Message: {2}. Stack Trace: {3} + Sınıf Temizleme metodu ({0}.{1}) başarısız oldu. Hata İletisi: {2}. Yığın İzlemesi: {3} + + + + Class Initialization method {0}.{1} threw exception. {2}: {3}. + Sınıf Başlatma metodu ({0}.{1}) özel durum oluşturdu. {2}: {3}. + + + + Method {0}.{1} has wrong signature. The method must be static, public, does not return a value and should not take any parameter. Additionally, if you are using async-await in method then return-type must be 'Task' or 'ValueTask'. + {0}.{1} yönteminin imzası yanlış. Yöntem statik, genel, değer döndürmeyen bir yöntem olmalı, hiçbir parametre almamalıdır. Bunlara ek olarak, yöntemde async-await kullanıyorsanız return-type değeri 'Task' veya 'ValueTask' olmalıdır. + + + + Method {0}.{1} has wrong signature. The method must be static, public, does not return a value and should take a single parameter of type TestContext. Additionally, if you are using async-await in method then return-type must be 'Task' or 'ValueTask'. + {0}.{1} yönteminin imzası yanlış. Yöntem statik, genel, değer döndürmeyen bir yöntem olmalı ve TestContext türünde tek bir parametre almalıdır. Bunlara ek olarak, yöntemde async-await kullanıyorsanız return-type değeri 'Task' veya 'ValueTask' olmalıdır. + + + + TestCleanup method {0}.{1} threw exception. {2}. + TestCleanup metodu {0}.{1} özel durum oluşturdu. {2}. + + + + Error calling Test Cleanup method for test class {0}: {1} + {0} test sınıfı için Test Temizleme metodu çağrılırken hata oluştu: {1} + + + + TestCleanup Stack Trace + TestCleanup Yığın İzleme + + Data source '{0}' cannot be found in the test configuration settings Test yapılandırması ayarlarında '{0}' adlı veri kaynağı bulunamıyor + + --- End of inner exception stack trace --- + --- İç özel durum yığın izlemesinin sonu --- + + The unit test adapter failed to connect to the data source or to read the data. For more information on troubleshooting this error, see "Troubleshooting Data-Driven Unit Tests" (http://go.microsoft.com/fwlink/?LinkId=62412) in the MSDN Library. Error details: {0} Birim test bağdaştırıcısı, veri kaynağına bağlanamadı veya verileri okuyamadı. Bu hata ile ilgili daha fazla sorun giderme bilgisi için MSDN Kitaplığı'ndaki "Veri Temelli Birim Testleriyle İlgili Sorunları Giderme" (http://go.microsoft.com/fwlink/?LinkId=62412) konusuna bakın. Hata ayrıntıları: {0} + + UTA031: class {0} does not have valid TestContext property. TestContext must be of type TestContext, must be non-static, and must be public. For example: public TestContext TestContext. + UTA031: {0}sınıfı geçerli bir TestContext özelliğine sahip değil. TestContext, TestContext türünde olmalı, static olmamalı ve public olmalıdır. Örnek: public TestContext TestContext. + + + + UTA007: Method {1} defined in class {0} does not have correct signature. Test method marked with the [TestMethod] attribute must be non-static, public, return-type as void and should not take any parameter. Example: public void Test.Class1.Test(). Additionally, if you are using async-await in test method then return-type must be 'Task' or 'ValueTask'. Example: public async Task Test.Class1.Test2() + UTA007: {0} sınıfında tanımlanan {1} yönteminin imzası doğru değil. [TestMethod] özniteliğiyle işaretlenmiş test yöntemi statik olmayan, genel, dönüş türü void olan bir yöntem olmalıdır ve hiçbir parametre almamalıdır. Örnek: public void Test.Class1.Test(). Bunlara ek olarak, test metodunda async-await kullanıyorsanız return-type değeri 'Task' veya 'ValueTask' olmalıdır. Örnek: public async Task Test.Class1.Test2() + + + + UTA054: {0}.{1} has invalid Timeout attribute. The timeout must be an integer value greater than 0. + UTA054: {0}.{1} Timeout özniteliği geçersiz. Zaman aşımı değeri 0'dan büyük bir tamsayı olmalıdır. + + + + UTA014: {0}: Cannot define more than one method with the AssemblyCleanup attribute inside an assembly. + UTA014: {0}: Bir bütünleştirilmiş kod içinde AssemblyCleanup özniteliği ile birden fazla metot tanımlanamaz. + + + + UTA013: {0}: Cannot define more than one method with the AssemblyInitialize attribute inside an assembly. + UTA013: {0}: Bir bütünleştirilmiş kod içinde AssemblyInitialize özniteliği ile birden fazla metot tanımlanamaz. + + + + UTA026: {0}: Cannot define more than one method with the ClassCleanup attribute inside a class. + UTA026: {0}: Bir sınıf içinde ClassCleanup özniteliği ile birden fazla metot tanımlanamaz. + + + + UTA025: {0}: Cannot define more than one method with the ClassInitialize attribute inside a class. + UTA025: {0}: Bir sınıf içinde ClassInitialize özniteliği ile birden fazla metot tanımlanamaz. + + + + UTA024: {0}: Cannot define more than one method with the TestCleanup attribute. + UTA024: {0}: TestCleanup özniteliği ile birden fazla metot tanımlanamaz. + + + + UTA018: {0}: Cannot define more than one method with the TestInitialize attribute. + UTA018: {0}: TestInitialize özniteliği ile birden fazla metot tanımlanamaz. + + + + UTA001: TestClass attribute defined on non-public class {0} + UTA001: TestClass özniteliği genel olmayan {0} sınıfında tanımlanmış + + + + UTA023: {0}: Cannot define predefined property {2} on method {1}. + UTA023: {0}: Önceden tanımlanmış {2} özelliği {1} metodunda tanımlanamaz. + + + + TestClass attribute defined on generic non-abstract class {0} + {0} genel soyut olmayan sınıf üzerinde tanımlanan TestClass özniteliği + + + + UTA021: {0}: Null or empty custom property defined on method {1}. The custom property must have a valid name. + UTA021: {0}: {1} metodunda null veya boş özel özellik tanımlanmış. Özel özellik geçerli bir ada sahip olmalıdır. + + + + An unhandled exception was thrown by the 'Execute' method. Please report this error to the author of the attribute '{0}'. +{1} + 'Execute' yöntemi tarafından işlenmeyen bir özel durum oluştu. Lütfen bu hatayı '{0}' özniteliğinin yazarına bildirin. +{1} + + + + The ExpectedException attribute defined on test method {0}.{1} threw an exception during construction. +{2} + {0}.{1} test yöntemi üzerinde tanımlanan ExpectedException özniteliği, oluşturma sırasında bir özel durum oluşturdu. +{2} + + + + Failed to obtain the exception thrown by test method {0}.{1}. + {0}.{1} metodu tarafından oluşturulan özel durum alınamadı. + + + + Initialization method {0}.{1} threw exception. {2}. + Başlatma metodu {0}.{1} özel durum oluşturdu. {2}. + + + + Unable to create instance of class {0}. Error: {1}. + {0} sınıfının örneği oluşturulamıyor. Hata: {1}. + + + + Method {0}.{1} does not exist. + {0}.{1} metodu yok. + + + + The test method '{0}.{1}' has multiple attributes derived from '{2}' defined on it. Only one such attribute is allowed. + “{0}.{1}” test yöntemi, üzerinde tanımlanan “{2}” öğesinden türetilmiş birden fazla öznitelik içeriyor. Bu türde yalnızca bir tane özniteliğe izin verilir. + + + + Error in executing test. No result returned by extension. If using extension of TestMethodAttribute then please contact vendor. + Test yürütülürken hata oluştu. Uzantı tarafından hiç sonuç döndürülmedi. TestMethodAttribute uzantısı kullanılıyorsa, lütfen satıcıya başvurun. + + + + Cannot find a valid constructor for test class '{0}'. Valid constructors are 'public' and either parameterless or with one parameter of type 'TestContext'. + '{0}' test sınıfı için geçerli bir oluşturucu bulunamıyor. Geçerli oluşturucular 'public' ve parametresiz veya 'TestContext' türünde tek bir parametre içeriyor. + + + + Unable to find property {0}.TestContext. Error:{1}. + {0}.TestContext özelliği bulunamıyor. Hata:{1}. + + + + Unable to set TestContext property for the class {0}. Error: {1}. + {0} sınıfı için TestContext özelliği ayarlanamıyor. Hata: {1}. + + + + The {0}.TestContext has incorrect type. + {0}.TestContext yanlış türe sahip. + + + + Method {0}.{1} has wrong signature. The method must be non-static, public, does not return a value and should not take any parameter. Additionally, if you are using async-await in method then return-type must be 'Task' or 'ValueTask'. + {0}.{1} yönteminin imzası yanlış. Yöntem statik olmayan, genel, değer döndürmeyen bir yöntem olmalı, hiçbir parametre almamalıdır. Bunlara ek olarak, yöntemde async-await kullanıyorsanız return-type değeri 'Task' veya 'ValueTask' olmalıdır. + + + + Test method {0}.{1} threw exception: +{2} + {0}.{1} test metodu özel durum oluşturdu: +{2} + + + + Unable to get type {0}. Error: {1}. + {0} türü alınamıyor. Hata: {1}. + + + + The called code threw an exception that was caught, but the exception value was null + Çağrılan kod, yakalanan bir özel durum yarattı, ancak özel durum değeri boştu + + + + {0} For UWP projects, if you are using UI objects in test consider using [UITestMethod] attribute instead of [TestMethod] to execute test in UI thread. + {0} UWP projeleri için testte UI nesneleri kullanıyorsanız, testi UI iş parçacığında yürütmek için [TestMethod] yerine [UITestMethod] özniteliğini kullanabilirsiniz. + + + + (Failed to get the message for an exception of type {0} due to an exception.) + (Bir özel durum nedeniyle, {0} türündeki özel durum iletisi alınamadı.) + + + + 'MSTest.TestAdapter' and 'MSTest.TestFramework' must have the same version. Found 'MSTest.TestAdapter' version '{0}' and 'MSTest.TestFramework' version '{1}'. Please make sure that the versions of 'MSTest.TestAdapter' and 'MSTest.TestFramework' NuGet packages have the same version. + 'MSTest.TestAdapter' ve 'MSTest.TestFramework' aynı sürüme sahip olmalıdır. 'MSTest.TestAdapter' sürümü '{0}' ve 'MSTest.TestFramework' sürümü '{1}' bulundu. Lütfen 'MSTest.TestAdapter' ve 'MSTest.TestFramework' NuGet paketlerinin aynı sürüme sahip olduğundan emin olun. + + Wrong number of objects for permutation. Should be greater than zero. Permütasyon için nesne sayısı yanlış. Değer, sıfırdan büyük olmalıdır. diff --git a/src/Adapter/MSTestAdapter.PlatformServices/Resources/xlf/Resource.zh-Hans.xlf b/src/Adapter/MSTestAdapter.PlatformServices/Resources/xlf/Resource.zh-Hans.xlf index 813ddf8f72..5cc1e12a73 100644 --- a/src/Adapter/MSTestAdapter.PlatformServices/Resources/xlf/Resource.zh-Hans.xlf +++ b/src/Adapter/MSTestAdapter.PlatformServices/Resources/xlf/Resource.zh-Hans.xlf @@ -2,16 +2,116 @@ + + Assembly cleanup method '{0}.{1}' timed out after {2}ms + 程序集清理方法“{0}.{1}”在 {2} ms 后超时 + + + + Assembly cleanup method '{0}.{1}' was canceled + 已取消程序集清理方法“{0}.{1}” + + + + Assembly initialize method '{0}.{1}' timed out after {2}ms + 程序集初始化方法“{0}.{1}”在 {2} ms 后超时 + + + + Assembly initialize method '{0}.{1}' was canceled + 已取消程序集初始化方法“{0}.{1}” + + + + MSTestAdapterV2 + MSTestAdapterV2 + + + + Exception occurred while enumerating IDataSource attribute on "{0}.{1}": {2} + 枚举 {0} 上的 IDataSource 属性时发生异常。{1}": {2} + {0}: TypeName with namespace, +{1}: Method name, +{2}: Exception details + + + Exception occurred while expanding IDataSource rows from attribute on "{0}.{1}": {2} + 从“{0}.{1}”上的属性扩展 IDataSource 行时出现异常: {2} + {0}: TypeName with namespace, +{1}: Method name, +{2}: CannotExpandIDataSourceAttribute_DuplicateDisplayName or CannotExpandIDataSourceAttribute_CannotSerialize + + + Data on index {0} for "{1}" cannot be serialized. All data provided through "IDataSource" should be serializable. If you need to test non-serializable data sources, please make sure you add "TestDataSourceDiscovery" attribute on your test assembly and set the discovery option to "DuringExecution". + 无法序列化“{1}”的索引 {0} 上的数据。通过“IDataSource”提供的所有数据都应可序列化。如果需要测试不可序列化的数据源,请确保在测试程序集上添加“TestDataSourceDiscovery”属性,并将发现选项设置为“DuringExecution”。 + {0}: Zero based index if an element inside of an array, +{1}: Test name + + + Display name "{2}" on indexes {0} and {1} are duplicate. Display names should be unique. + 索引 {0} 和 {1} 上的显示名称“{2}”重复。显示名称应是唯一的。 + {0}, {1}: Zero based index if an element inside of an array +{2}: Test display name. + Could not find file '{0}'. 找不到文件“{0}”。 + + Cannot run test method '{0}.{1}': Test data doesn't match method parameters. Either the count or types are different. +Test expected {2} parameter(s), with types '{3}', +but received {4} argument(s), with types '{5}'. + 无法运行测试方法“{0}.{1}”: 测试数据与方法参数不匹配。计数或类型不同。 +测试需要类型为“{3}”的 {2} 参数, +但收到了类型为“{5}”的 {4} 参数。 + + + + Cannot run test method '{0}.{1}': Method has parameters, but does not define any test source. Use '[DataRow]', '[DynamicData]', or a custom 'ITestDataSource' data source to provide test data. + 无法运行测试方法“{0}.{1}”: 方法具有参数,但未定义任何测试源。使用 “[DataRow]”、“[DynamicData]” 或自定义 “ITestDataSource” 数据源提供测试数据。 + + + + Class cleanup method '{0}.{1}' timed out after {2}ms + 类清理方法“{0}.{1}”在 {2} ms 后超时 + + + + Class cleanup method '{0}.{1}' was canceled + 已取消类清理方法“{0}.{1}” + + + + Class initialize method '{0}.{1}' timed out after {2}ms + 类初始化方法“{0}.{1}”在 {2} ms 后超时 + + + + Class initialize method '{0}.{1}' was canceled + 已取消类初始化方法“{0}.{1}” + + The parameter should not be null or empty. 参数不应为 NULL 或为空。 + + MSTestAdapter failed to discover tests in class '{0}' of assembly '{1}' because {2}. + 由于 {2},MSTestAdapter 未能在程序集“{1}”的类“{0}”中发现测试。 + + + + {0} (Data Row {1}) + {0} (数据行 {1}) + + + + Debug Trace: + 调试跟踪: + + Test Run deployment issue: Bad deployment item: '{0}': output directory '{1}' specifies the item to be deployed outside deployment root directory which is not allowed. 测试运行部署问题: 错误的部署项:“{0}”: 输出目录“{1}”指定将该项部署到部署根目录的外部,这是不允许的。 @@ -77,16 +177,108 @@ 部署项“{0}”(输出目录“{1}”) + + [MSTest][Discovery][{0}] {1} + [MSTest][发现][{0}] {1} + + + + Both '.runsettings' and '.testconfig.json' files have been detected. Please select only one of these test configuration files. + 检测到 ".runsettings" 和 ".testconfig.json" 文件。请仅选择其中一个测试配置文件。 + + + + {0}: {1} + {0}: {1} + + + + "{0}": (Failed to get exception description due to an exception of type "{1}". + “{0}”:(由于类型“{1}”异常,无法获取异常说明。 + {0}: Type of the original exception that we're trying to get the description of. +{1}: Thrown exception + + + Exceptions thrown: + 引发的异常: + This is usually precedes by TestAssembly_AssemblyDiscoveryFailure message, and preceded by list of exceptions thrown in a test discovery session. + + + Test '{0}' was canceled + 测试“{0}”已取消 + + + + Test '{0}' timed out after {1}ms + 测试“{0}”在 {1} 毫秒后超时 + + + + Getting custom attributes for type {0} threw exception (will ignore and use the reflection way): {1} + 获取类型 {0} 自定义属性引发异常(将忽略并使用反射方式): {1} + {0}: Attribute full type name. +{1}: Exception description + + + The type of the generic parameter '{0}' could not be inferred. + 无法推断泛型参数“{0}”的类型。 + + + + The generic test method '{0}' doesn't have arguments, so the generic parameter cannot be inferred. + 泛型测试方法“{0}”没有参数,因此无法推断泛型参数。 + + + + Found two conflicting types for generic parameter '{0}'. The conflicting types are '{1}' and '{2}'. + 发现泛型参数“{0}”有两种冲突类型。冲突类型为“{1}”和“{2}”。 + + + + Invalid value '{0}' specified for 'ClassCleanupLifecycle'. Supported scopes are {1}. + 为“ClassCleanupLifecycle”指定的值“{0}”无效。支持的作用域为 {1}。 + {Locked="ClassCleanupLifecycle"} + + + Invalid value '{0}' specified for 'Scope'. Supported scopes are {1}. + 为 ‘Scope’ 指定的值 ‘{0}’ 无效。受支持的范围为 {1}。 + {Locked="Scope"} + + + Invalid value '{0}' specified for 'Workers'. The value should be a non-negative integer. + 为 ‘Workers’ 指定的值 ‘{0}’ 无效。该值应为非负整数。 + {Locked="Workers"} + + + Invalid settings '{0}'. Unexpected XmlAttribute: '{1}'. + 设置“{0}”无效。意外的 XmlAttribute:“{1}”。 + + MSTestAdapter encountered an unexpected element '{0}' in its settings '{1}'. Remove this element and try again. MSTestAdapter 在其设置“{1}”中遇到意外的元素“{0}”。删除此元素,然后再试一次。 + + Invalid settings '{0}'. Unexpected XmlElement: '{1}'. + 设置“{0}”无效。意外的 XmlElement:“{1}”。 + + Invalid value '{0}' for runsettings entry '{1}', setting will be ignored. runsettings 项 ‘{1}’ 的值 ‘{0}’ 无效,将忽略设置。 + + Invalid value '{0}' for runsettings entry '{1}', setting will be ignored. + runsettings 项 ‘{1}’ 的值 ‘{0}’ 无效,将忽略设置。 + + + + Warning : A testsettings file or a vsmdi file is not supported with the MSTest V2 Adapter. + 警告: MSTest V2 适配器不支持 testsettings 文件或 vsmdi 文件。 + + Test Run deployment issue: The assembly or module '{0}' was not found. Reason: {1} 测试运行部署问题: 找不到程序集或模块“{0}”。原因: {1} @@ -97,21 +289,309 @@ 测试运行部署问题: 找不到程序集或模块“{0}”。 + + An older version of MSTestV2 package is loaded in assembly, test discovery might fail to discover all data tests if they depend on `.runsettings` file. + 程序集中加载了 MSTestV2 包的较旧版本,如果测试发现依赖于“.runsettings”文件,则它们可能无法发现所有数据测试。 + + + + Runsettings entry '<ExecutionApartmentState>STA</ExecutionApartmentState>' is not supported on non-Windows OSes. + 非 Windows 操作系统不支持 Runsettings 条目 "<ExecutionApartmentState>STA</ExecutionApartmentState>"。 + + + + Running tests in any of the provided sources is not supported for the selected platform + 选定的平台不支持在任何提供的源中运行测试 + + + + Failed to discover tests from assembly {0}. Reason:{1} + 未能发现程序集 {0} 中的测试。原因: {1} + + + + File does not exist: {0} + 文件不存在: {0} + + + + Test cleanup method '{0}.{1}' timed out after {2}ms + 测试清理方法“{0}.{1}”在 {2} ms 后超时 + + + + Test cleanup method '{0}.{1}' was canceled + 已取消测试清理方法“{0}.{1}” + + + + TestContext cannot be Null. + TestContext 不能为 NULL。 + + + + TestContext Messages: + TestContext 消息: + + + + Test initialize method '{0}.{1}' timed out after {2}ms + 测试初始化方法“{0}.{1}”在 {2} ms 后超时 + + + + Test initialize method '{0}.{1}' was canceled + 已取消测试初始化方法“{0}.{1}” + + + + Test method {0} was not found. + 未找到测试方法 {0}。 + + + + Test Parallelization enabled for {0} (Workers: {1}, Scope: {2}) + 已为 {0} 启用测试并行化(Workers: {1},Scope: {2}) + {Locked="Workers"}{Locked="Scope"} + {0}_{1} {2} {0}_{1} {2} + + Unable to load types from the test source '{0}'. Some or all of the tests in this source may not be discovered. +Error: {1} + 无法从测试源“{0}”加载类型。可能无法发现此源中的部分或所有测试。 +错误: {1} + + + + Assembly Cleanup method {0}.{1} failed. Error Message: {2}. StackTrace: {3} + 程序集清理方法 {0}.{1} 失败。错误消息: {2}。StackTrace: {3} + + + + Assembly Initialization method {0}.{1} threw exception. {2}: {3}. Aborting test execution. + 程序集初始化方法 {0}.{1} 引发异常。{2}: {3}。正在中止测试的执行。 + + + + Class Cleanup method {0}.{1} failed. Error Message: {2}. Stack Trace: {3} + 类清理方法 {0}.{1} 失败。错误消息: {2}。堆栈跟踪: {3} + + + + Class Initialization method {0}.{1} threw exception. {2}: {3}. + 类初始化方法 {0}.{1} 引发异常。{2}: {3}。 + + + + Method {0}.{1} has wrong signature. The method must be static, public, does not return a value and should not take any parameter. Additionally, if you are using async-await in method then return-type must be 'Task' or 'ValueTask'. + 方法 {0}。{1}的签名错误。该方法必须是静态的公共方法、不返回值并且不应采用任何参数。此外,如果在方法中使用同步等待,则返回类型必须为“Task”或“Value Task”。 + + + + Method {0}.{1} has wrong signature. The method must be static, public, does not return a value and should take a single parameter of type TestContext. Additionally, if you are using async-await in method then return-type must be 'Task' or 'ValueTask'. + 方法 {0}。{1}的签名错误。该方法必须是静态的公共方法,不返回值,并且应采用一个 TestContext 类型的参数。此外,如果在方法中使用同步等待,则返回类型必须为“Task”或“Value Task”。 + + + + TestCleanup method {0}.{1} threw exception. {2}. + TestCleanup 方法 {0}.{1} 引发异常。{2}。 + + + + Error calling Test Cleanup method for test class {0}: {1} + 为测试类 {0} 调用 Test Cleanup 方法时出错: {1} + + + + TestCleanup Stack Trace + TestCleanup 堆栈跟踪 + + Data source '{0}' cannot be found in the test configuration settings 在测试配置设置中找不到数据源“{0}” + + --- End of inner exception stack trace --- + ---内部异常堆栈跟踪结束--- + + The unit test adapter failed to connect to the data source or to read the data. For more information on troubleshooting this error, see "Troubleshooting Data-Driven Unit Tests" (http://go.microsoft.com/fwlink/?LinkId=62412) in the MSDN Library. Error details: {0} 单元测试适配器未能连接到数据源或读取数据。有关解决此错误的详细信息,请查看 MSDN 库中的“数据驱动单元测试疑难解答”(http://go.microsoft.com/fwlink/?LinkId=62412)。错误详细信息: {0} + + UTA031: class {0} does not have valid TestContext property. TestContext must be of type TestContext, must be non-static, and must be public. For example: public TestContext TestContext. + UTA031: 类 {0} 没有有效的 TestContext 属性。TestContext 必须是 TestContext 类型并且必须是非静态和公共的。例如: public TestContext TestContext。 + + + + UTA007: Method {1} defined in class {0} does not have correct signature. Test method marked with the [TestMethod] attribute must be non-static, public, return-type as void and should not take any parameter. Example: public void Test.Class1.Test(). Additionally, if you are using async-await in test method then return-type must be 'Task' or 'ValueTask'. Example: public async Task Test.Class1.Test2() + UTA007: 在类 {0} 中定义的方法 {1} 没有正确的签名。用 [TestMethod] 特性标记的测试方法必须是返回类型为 void 的非静态的公共方法,并且不应采用任何参数。示例: public void Test.Class1.Test()。此外,如果在测试方法中使用同步等待,则返回类型必须为“Task”或“Value Task”。示例: public async Task Test.Class1.Test2() + + + + UTA054: {0}.{1} has invalid Timeout attribute. The timeout must be an integer value greater than 0. + UTA054: {0}。{1} 的超时属性无效。“超时”必须是大于零的整数值。 + + + + UTA014: {0}: Cannot define more than one method with the AssemblyCleanup attribute inside an assembly. + UTA014: {0}: 在一个程序集内部,不能定义多个具有 AssemblyCleanup 特性的方法。 + + + + UTA013: {0}: Cannot define more than one method with the AssemblyInitialize attribute inside an assembly. + UTA013: {0}: 在一个程序集内部,不能定义多个具有 AssemblyInitialize 特性的方法。 + + + + UTA026: {0}: Cannot define more than one method with the ClassCleanup attribute inside a class. + UTA026: {0}: 在一个类内部,不能定义多个具有 ClassCleanup 特性的方法。 + + + + UTA025: {0}: Cannot define more than one method with the ClassInitialize attribute inside a class. + UTA025: {0}: 在一个类内部,不能定义多个具有 ClassInitialize 特性的方法。 + + + + UTA024: {0}: Cannot define more than one method with the TestCleanup attribute. + UTA024: {0}: 不能定义多个具有 TestCleanup 特性的方法。 + + + + UTA018: {0}: Cannot define more than one method with the TestInitialize attribute. + UTA018: {0}: 不能定义多个具有 TestInitialize 特性的方法。 + + + + UTA001: TestClass attribute defined on non-public class {0} + UTA001: 在非公共类 {0} 上定义的 TestClass 特性 + + + + UTA023: {0}: Cannot define predefined property {2} on method {1}. + UTA023: {0}: 不能在方法 {1} 上定义预定义属性 {2}。 + + + + TestClass attribute defined on generic non-abstract class {0} + 在泛型非抽象类 {0} 上定义的 TestClass 特性 + + + + UTA021: {0}: Null or empty custom property defined on method {1}. The custom property must have a valid name. + UTA021: {0}: 对方法 {1} 定义了为 NULL 或为空的自定义属性。自定义属性必须具有有效名称。 + + + + An unhandled exception was thrown by the 'Execute' method. Please report this error to the author of the attribute '{0}'. +{1} + “Execute”方法引发了未经处理的异常。请将此错误报告给属性“{0}”的作者。 +{1} + + + + The ExpectedException attribute defined on test method {0}.{1} threw an exception during construction. +{2} + 测试方法 {0}.{1} 上定义的 ExpectedException 属性在构造过程中引发了一个异常。 +{2} + + + + Failed to obtain the exception thrown by test method {0}.{1}. + 未能获取测试方法 {0}.{1} 引发的异常。 + + + + Initialization method {0}.{1} threw exception. {2}. + 初始化方法 {0}.{1} 引发异常。{2}。 + + + + Unable to create instance of class {0}. Error: {1}. + 无法创建类 {0} 的实例。错误: {1}。 + + + + Method {0}.{1} does not exist. + 方法 {0}.{1} 不存在。 + + + + The test method '{0}.{1}' has multiple attributes derived from '{2}' defined on it. Only one such attribute is allowed. + 测试方法“{0}.{1}”具有多个在其上定义的“{2}”的派生属性。仅允许一个此类属性。 + + + + Error in executing test. No result returned by extension. If using extension of TestMethodAttribute then please contact vendor. + 执行测试时出错。扩展未返回任何结果。如果使用的是扩展 TestMethodAttribute ,请与供应商联系。 + + + + Cannot find a valid constructor for test class '{0}'. Valid constructors are 'public' and either parameterless or with one parameter of type 'TestContext'. + 找不到测试类“{0}”的有效构造函数。有效的构造函数为 “public”,但该构造函数无参数或具有一个类型为 “TestContext” 的参数。 + + + + Unable to find property {0}.TestContext. Error:{1}. + 无法找到属性 {0}.TestContext。错误: {1}。 + + + + Unable to set TestContext property for the class {0}. Error: {1}. + 无法设置类 {0} 的 TestContext 属性。错误: {1}。 + + + + The {0}.TestContext has incorrect type. + {0}.TestContext 的类型不正确。 + + + + Method {0}.{1} has wrong signature. The method must be non-static, public, does not return a value and should not take any parameter. Additionally, if you are using async-await in method then return-type must be 'Task' or 'ValueTask'. + 方法 {0}。{1}的签名错误。该方法必须是非静态的公共方法、不返回值并且不应采用任何参数。此外,如果在方法中使用同步等待,则返回类型必须为“Task”或“Value Task”。 + + + + Test method {0}.{1} threw exception: +{2} + 测试方法 {0}.{1} 引发了异常: +{2} + + + + Unable to get type {0}. Error: {1}. + 无法获取类型 {0}。错误: {1}。 + + + + The called code threw an exception that was caught, but the exception value was null + 调用的代码引发了捕获的异常,但异常值为 null + + + + {0} For UWP projects, if you are using UI objects in test consider using [UITestMethod] attribute instead of [TestMethod] to execute test in UI thread. + {0} 对于 UWP 项目,如果在测试中使用 UI 对象,请考虑使用 [UITestMethod] 属性代替 [TestMethod] 属性在 UI 线程中执行测试。 + + + + (Failed to get the message for an exception of type {0} due to an exception.) + (因异常而未能获取类型为 {0} 的异常的消息。) + + + + 'MSTest.TestAdapter' and 'MSTest.TestFramework' must have the same version. Found 'MSTest.TestAdapter' version '{0}' and 'MSTest.TestFramework' version '{1}'. Please make sure that the versions of 'MSTest.TestAdapter' and 'MSTest.TestFramework' NuGet packages have the same version. + 'MSTest.TestAdapter' 和 'MSTest.TestFramework' 必须具有相同的版本。找到 'MSTest.TestAdapter' 版本“{0}”和 'MSTest.TestFramework' 版本“{1}”。请确保 'MSTest.TestAdapter' 和 'MSTest.TestFramework' NuGet 包的版本具有相同的版本。 + + Wrong number of objects for permutation. Should be greater than zero. 排列的对象数不正确。应大于零。 diff --git a/src/Adapter/MSTestAdapter.PlatformServices/Resources/xlf/Resource.zh-Hant.xlf b/src/Adapter/MSTestAdapter.PlatformServices/Resources/xlf/Resource.zh-Hant.xlf index aae5f14299..d5a3738734 100644 --- a/src/Adapter/MSTestAdapter.PlatformServices/Resources/xlf/Resource.zh-Hant.xlf +++ b/src/Adapter/MSTestAdapter.PlatformServices/Resources/xlf/Resource.zh-Hant.xlf @@ -2,16 +2,116 @@ + + Assembly cleanup method '{0}.{1}' timed out after {2}ms + 組件清理方法 '{0}.{1}' 在 {2} 毫秒後已逾時 + + + + Assembly cleanup method '{0}.{1}' was canceled + 已取消組件清理方法 '{0}.{1}' + + + + Assembly initialize method '{0}.{1}' timed out after {2}ms + 組件初始化方法 '{0}.{1}' 在 {2} 毫秒後已逾時 + + + + Assembly initialize method '{0}.{1}' was canceled + 已取消組件初始化方法 '{0}.{1}' + + + + MSTestAdapterV2 + MSTestAdapterV2 + + + + Exception occurred while enumerating IDataSource attribute on "{0}.{1}": {2} + 列舉「{0}.{1}」上的 IDataSource 屬性時發生例外狀況: {2} + {0}: TypeName with namespace, +{1}: Method name, +{2}: Exception details + + + Exception occurred while expanding IDataSource rows from attribute on "{0}.{1}": {2} + 從「{0}.{1}」上的屬性展開 IDataSource 資料列時發生例外狀況: {2} + {0}: TypeName with namespace, +{1}: Method name, +{2}: CannotExpandIDataSourceAttribute_DuplicateDisplayName or CannotExpandIDataSourceAttribute_CannotSerialize + + + Data on index {0} for "{1}" cannot be serialized. All data provided through "IDataSource" should be serializable. If you need to test non-serializable data sources, please make sure you add "TestDataSourceDiscovery" attribute on your test assembly and set the discovery option to "DuringExecution". + 無法序列化「{1}"」索引 {0} 上的資料。透過「IDataSource」提供的所有資料應可序列化。如果您需要測試不可序列化的資料來源,請務必在測試元件上新增「TestDataSourceDiscovery」屬性,並將探索選項設定為「DuringExecution」。 + {0}: Zero based index if an element inside of an array, +{1}: Test name + + + Display name "{2}" on indexes {0} and {1} are duplicate. Display names should be unique. + 索引 {0} 和 {1} 上的顯示名稱「{2}」重複。顯示名稱必須是唯一的。 + {0}, {1}: Zero based index if an element inside of an array +{2}: Test display name. + Could not find file '{0}'. 找不到檔案 '{0}'。 + + Cannot run test method '{0}.{1}': Test data doesn't match method parameters. Either the count or types are different. +Test expected {2} parameter(s), with types '{3}', +but received {4} argument(s), with types '{5}'. + 無法執行測試方法 '{0}.{1}': 測試資料不符合方法參數。計數或類型不同。 +測試預期的 {2} 參數,類型為 '{3}', +但收到 {4} 引數,類型為 '{5}'。 + + + + Cannot run test method '{0}.{1}': Method has parameters, but does not define any test source. Use '[DataRow]', '[DynamicData]', or a custom 'ITestDataSource' data source to provide test data. + 無法執行測試方法 '{0}.{1}': 方法具有參數,但未定義任何測試來源。使用 '[DataRow]'、'[DynamicData]' 或自訂 'ITestDataSource' 資料來源來提供測試資料。 + + + + Class cleanup method '{0}.{1}' timed out after {2}ms + 類別清理方法 '{0}.{1}' 在 {2} 毫秒後已逾時 + + + + Class cleanup method '{0}.{1}' was canceled + 已取消類別清理方法 '{0}.{1}' + + + + Class initialize method '{0}.{1}' timed out after {2}ms + 類別初始化方法 '{0}.{1}' 在 {2} 毫秒後已逾時 + + + + Class initialize method '{0}.{1}' was canceled + 已取消類別初始化方法 '{0}.{1}' + + The parameter should not be null or empty. 參數不可為 null 或空白。 + + MSTestAdapter failed to discover tests in class '{0}' of assembly '{1}' because {2}. + MSTestAdapter 無法在組件 '{1}' 的類別 '{0}' 中探索測試,因為 {2}。 + + + + {0} (Data Row {1}) + {0} (資料列 {1}) + + + + Debug Trace: + 偵錯追蹤: + + Test Run deployment issue: Bad deployment item: '{0}': output directory '{1}' specifies the item to be deployed outside deployment root directory which is not allowed. 測試回合部署問題: 部署項目錯誤: '{0}': 輸出目錄 '{1}' 指定要將項目部署到部署根目錄之外,但不允許這情況。 @@ -77,16 +177,108 @@ 部署項目 '{0}' (輸出目錄 '{1}') + + [MSTest][Discovery][{0}] {1} + [MSTest][Discovery][{0}] {1} + + + + Both '.runsettings' and '.testconfig.json' files have been detected. Please select only one of these test configuration files. + 偵測到 '.runsettings' 和 '.testconfig.json' 檔案。請只選取其中一個測試設定檔。 + + + + {0}: {1} + {0}: {1} + + + + "{0}": (Failed to get exception description due to an exception of type "{1}". + 「{0}」: (因為類型「{1}」的例外狀況而無法取得例外狀況描述。 + {0}: Type of the original exception that we're trying to get the description of. +{1}: Thrown exception + + + Exceptions thrown: + 擲回的例外狀況數: + This is usually precedes by TestAssembly_AssemblyDiscoveryFailure message, and preceded by list of exceptions thrown in a test discovery session. + + + Test '{0}' was canceled + 測試 '{0}' 已取消 + + + + Test '{0}' timed out after {1}ms + 測試 '{0}' 在 {1} 毫秒後逾時 + + + + Getting custom attributes for type {0} threw exception (will ignore and use the reflection way): {1} + 取得類型 {0} 擲回例外狀況的自訂屬性 (將會略過並使用反映方式): {1} + {0}: Attribute full type name. +{1}: Exception description + + + The type of the generic parameter '{0}' could not be inferred. + 無法推斷泛型參數 '{0}' 的類型。 + + + + The generic test method '{0}' doesn't have arguments, so the generic parameter cannot be inferred. + 泛型測試方法 '{0}' 沒有引數,因此無法推斷泛型參數。 + + + + Found two conflicting types for generic parameter '{0}'. The conflicting types are '{1}' and '{2}'. + 發現泛型參數 '{0}' 有兩個衝突類型。衝突類型為 '{1}' 和 '{2}'。 + + + + Invalid value '{0}' specified for 'ClassCleanupLifecycle'. Supported scopes are {1}. + 為 'ClassCleanupLifecycle' 指定的值 '{0}' 無效。支援的範圍為 {1}。 + {Locked="ClassCleanupLifecycle"} + + + Invalid value '{0}' specified for 'Scope'. Supported scopes are {1}. + 為 'Scope' 指定的值 '{0}' 無效。支援的範圍為 {1}。 + {Locked="Scope"} + + + Invalid value '{0}' specified for 'Workers'. The value should be a non-negative integer. + 為 'Workers' 的值 '{0}' 無效。值應為非負整數。 + {Locked="Workers"} + + + Invalid settings '{0}'. Unexpected XmlAttribute: '{1}'. + 設定 '{0}' 無效。未預期的 XmlAttribute: '{1}'。 + + MSTestAdapter encountered an unexpected element '{0}' in its settings '{1}'. Remove this element and try again. MSTestAdapter 在其設定 '{1}' 中遇到未預期的項目 '{0}'。請移除此項目,然後再試一次。 + + Invalid settings '{0}'. Unexpected XmlElement: '{1}'. + 設定 '{0}' 無效。未預期的 XmlElement: '{1}'。 + + Invalid value '{0}' for runsettings entry '{1}', setting will be ignored. runsettings 項目 '{1}' 的值 '{0}' 無效,將忽略設定。 + + Invalid value '{0}' for runsettings entry '{1}', setting will be ignored. + runsettings 項目 '{1}' 的值 '{0}' 無效,將忽略設定。 + + + + Warning : A testsettings file or a vsmdi file is not supported with the MSTest V2 Adapter. + 警告: MSTest V2 配接器不支援 testsettings 檔案 vsmdi 檔案。 + + Test Run deployment issue: The assembly or module '{0}' was not found. Reason: {1} 測試回合部署問題: 找不到組件或模組 '{0}'。原因: {1} @@ -97,21 +289,309 @@ 測試回合部署問題: 找不到組件或模組 '{0}'。 + + An older version of MSTestV2 package is loaded in assembly, test discovery might fail to discover all data tests if they depend on `.runsettings` file. + 元件中已載入舊版的 MSTestV2 套件,如果測試探索相依於 '.runsettings' 檔案,則測試探索可能無法探索所有資料測試。 + + + + Runsettings entry '<ExecutionApartmentState>STA</ExecutionApartmentState>' is not supported on non-Windows OSes. + 非 Windows OS 不支援 Runsettings 項目 '<ExecutionApartmentState>STA</ExecutionApartmentState>'。 + + + + Running tests in any of the provided sources is not supported for the selected platform + 所選取的平台不支援在任何提供的來源中執行測試 + + + + Failed to discover tests from assembly {0}. Reason:{1} + 無法從組件 {0} 中探索測試。原因:{1} + + + + File does not exist: {0} + 檔案不存在: {0} + + + + Test cleanup method '{0}.{1}' timed out after {2}ms + 測試清理方法 '{0}.{1}' 在 {2} 毫秒後已逾時 + + + + Test cleanup method '{0}.{1}' was canceled + 已取消測試清理方法 '{0}.{1}' + + + + TestContext cannot be Null. + TestContext 不可為 Null。 + + + + TestContext Messages: + TestContext 訊息: + + + + Test initialize method '{0}.{1}' timed out after {2}ms + 測試初始化方法 '{0}.{1}' 在 {2} 毫秒後已逾時 + + + + Test initialize method '{0}.{1}' was canceled + 已取消測試初始化方法 '{0}.{1}' + + + + Test method {0} was not found. + 找不到測試方法 {0}。 + + + + Test Parallelization enabled for {0} (Workers: {1}, Scope: {2}) + 已為 {0} 啟用平行測試 (Workers: {1}, Scope: {2}) + {Locked="Workers"}{Locked="Scope"} + {0}_{1} {2} {0}_{1} {2} + + Unable to load types from the test source '{0}'. Some or all of the tests in this source may not be discovered. +Error: {1} + 無法從測試來源 '{0}' 載入類型。有可能無法探索此來源中的部分或所有測試。 +錯誤: {1} + + + + Assembly Cleanup method {0}.{1} failed. Error Message: {2}. StackTrace: {3} + 組件清除方法 {0}.{1} 失敗。錯誤訊息: {2}。堆疊追蹤: {3} + + + + Assembly Initialization method {0}.{1} threw exception. {2}: {3}. Aborting test execution. + 組件初始設定方法 {0}.{1} 擲回例外狀況。{2}: {3}。正在中止測試執行。 + + + + Class Cleanup method {0}.{1} failed. Error Message: {2}. Stack Trace: {3} + 類別清除方法 {0}.{1} 失敗。錯誤訊息: {2}。堆疊追蹤: {3} + + + + Class Initialization method {0}.{1} threw exception. {2}: {3}. + 類別初始設定方法 {0}.{1} 擲回例外狀況。{2}: {3}。 + + + + Method {0}.{1} has wrong signature. The method must be static, public, does not return a value and should not take any parameter. Additionally, if you are using async-await in method then return-type must be 'Task' or 'ValueTask'. + 方法 {0}.{1} 有錯誤的簽章。方法必須為靜態、公用、不傳回值,並且不應該接受任何參數。此外,如果您在方法中使用 async-await,則傳回類型必須是 'Task' 或 'ValueTask'。 + + + + Method {0}.{1} has wrong signature. The method must be static, public, does not return a value and should take a single parameter of type TestContext. Additionally, if you are using async-await in method then return-type must be 'Task' or 'ValueTask'. + 方法 {0}.{1} 有錯誤的簽章。方法必須為靜態、公用、不傳回值,並且應接受類型為 TestContext 的單一參數。此外,如果您在方法中使用 async-await,則傳回類型必須是 'Task' 或 'ValueTask'。 + + + + TestCleanup method {0}.{1} threw exception. {2}. + TestCleanup 方法 {0}.{1} 擲回例外狀況。{2}。 + + + + Error calling Test Cleanup method for test class {0}: {1} + 呼叫測試類別 {0} 的測試清除方法時發生錯誤: {1} + + + + TestCleanup Stack Trace + TestCleanup 堆疊追蹤 + + Data source '{0}' cannot be found in the test configuration settings 在測試組態設定中找不到資料來源 '{0}' + + --- End of inner exception stack trace --- + --- 已到達內部例外狀況堆疊追蹤的結尾 --- + + The unit test adapter failed to connect to the data source or to read the data. For more information on troubleshooting this error, see "Troubleshooting Data-Driven Unit Tests" (http://go.microsoft.com/fwlink/?LinkId=62412) in the MSDN Library. Error details: {0} 單元測試配接器無法連接至資料來源或無法讀取資料。如需為此錯誤進行疑難排解的詳細資訊,請參閱 MSDN Library 上的<如何: 建立資料驅動型單元測試>(http://go.microsoft.com/fwlink/?LinkId=62412)。錯誤詳細資料: {0} + + UTA031: class {0} does not have valid TestContext property. TestContext must be of type TestContext, must be non-static, and must be public. For example: public TestContext TestContext. + UTA031: 類別 {0}不具備有效的 TestContext 屬性。TestContext 必須是 TestContext 類型、必須是非靜態的,而且必須是公用的。例如: public TestContext TestContext。 + + + + UTA007: Method {1} defined in class {0} does not have correct signature. Test method marked with the [TestMethod] attribute must be non-static, public, return-type as void and should not take any parameter. Example: public void Test.Class1.Test(). Additionally, if you are using async-await in test method then return-type must be 'Task' or 'ValueTask'. Example: public async Task Test.Class1.Test2() + UTA007: 類別 {0} 中定義的方法 {1} 沒有正確的簽章。標記 [TestMethod] 屬性的測試方法必須為非靜態、公用、傳回類型為 void,而且不應該接受任何參數。範例: public void Test.Class1.Test()。此外,如果您在測試方法中使用 async-await,則傳回類型必須是 'Task' 或 'ValueTask'。範例: public async Task Test.Class1.Test2() + + + + UTA054: {0}.{1} has invalid Timeout attribute. The timeout must be an integer value greater than 0. + UTA054: {0}。{1} 中具有無效的 Timeout 屬性。逾時必須為大於 0 的整數值。 + + + + UTA014: {0}: Cannot define more than one method with the AssemblyCleanup attribute inside an assembly. + UTA014: {0}: 組件內不可定義一個以上具有 AssemblyCleanup 屬性的方法。 + + + + UTA013: {0}: Cannot define more than one method with the AssemblyInitialize attribute inside an assembly. + UTA013: {0}: 組件內不可定義一個以上具有 AssemblyInitialize 屬性的方法。 + + + + UTA026: {0}: Cannot define more than one method with the ClassCleanup attribute inside a class. + UTA026: {0}: 類別內不可定義一個以上具有 ClassCleanup 屬性的方法。 + + + + UTA025: {0}: Cannot define more than one method with the ClassInitialize attribute inside a class. + UTA025: {0}: 類別內不可定義一個以上具有 ClassInitialize 屬性的方法。 + + + + UTA024: {0}: Cannot define more than one method with the TestCleanup attribute. + UTA024: {0}: 不可定義一個以上具有 TestCleanup 屬性的方法。 + + + + UTA018: {0}: Cannot define more than one method with the TestInitialize attribute. + UTA018: {0}: 不可定義一個以上具有 TestInitialize 屬性的方法。 + + + + UTA001: TestClass attribute defined on non-public class {0} + UTA001: 在非公用類別 {0} 上定義了 TestClass 屬性 + + + + UTA023: {0}: Cannot define predefined property {2} on method {1}. + UTA023: {0}: 不可在方法 {1} 上定義預先定義的屬性 {2}。 + + + + TestClass attribute defined on generic non-abstract class {0} + 在一般非抽象類別上定義的 TestClass 屬性 {0} + + + + UTA021: {0}: Null or empty custom property defined on method {1}. The custom property must have a valid name. + UTA021: {0}: 在方法 {1} 上定義了 Null 或空白的自訂屬性。自訂屬性的名稱必須有效。 + + + + An unhandled exception was thrown by the 'Execute' method. Please report this error to the author of the attribute '{0}'. +{1} + 'Execute' 方法擲回未處理的例外狀況。請將此錯誤回報給屬性 '{0}' 的作者。 +{1} + + + + The ExpectedException attribute defined on test method {0}.{1} threw an exception during construction. +{2} + 測試方法 {0} 上定義的 ExpectedException 屬性。{1} 在建構期間擲回例外狀況。 +{2} + + + + Failed to obtain the exception thrown by test method {0}.{1}. + 無法取得測試方法 {0}.{1} 所擲回的例外狀況。 + + + + Initialization method {0}.{1} threw exception. {2}. + 初始設定方法 {0}.{1} 擲回例外狀況。{2}。 + + + + Unable to create instance of class {0}. Error: {1}. + 無法建立類別 {0} 的執行個體。錯誤: {1}。 + + + + Method {0}.{1} does not exist. + 方法 {0}.{1} 不存在。 + + + + The test method '{0}.{1}' has multiple attributes derived from '{2}' defined on it. Only one such attribute is allowed. + 測試方法 '{0}.{1}' 具有多個衍生自 '{2}' 的屬性根據其定義。只允許一個此類屬性。 + + + + Error in executing test. No result returned by extension. If using extension of TestMethodAttribute then please contact vendor. + 執行測試時發生錯誤。擴充功能未傳回任何結果。如果您使用 TestMethodAttribute 的擴充功能,請連絡廠商。 + + + + Cannot find a valid constructor for test class '{0}'. Valid constructors are 'public' and either parameterless or with one parameter of type 'TestContext'. + 找不到測試類別 '{0}' 的有效建構函式。有效的建構函式為 'public' 且無參數或具有一個類型為 'TestContext' 的參數。 + + + + Unable to find property {0}.TestContext. Error:{1}. + 找不到屬性 {0}.TestContext。錯誤: {1}。 + + + + Unable to set TestContext property for the class {0}. Error: {1}. + 無法設定類別 {0} 的 TestContext 屬性。錯誤: {1}。 + + + + The {0}.TestContext has incorrect type. + {0}.TestContext 有不正確的類型。 + + + + Method {0}.{1} has wrong signature. The method must be non-static, public, does not return a value and should not take any parameter. Additionally, if you are using async-await in method then return-type must be 'Task' or 'ValueTask'. + 方法 {0}.{1} 有錯誤的簽章。方法必須為非靜態、公用、不傳回值,並且不應該接受任何參數。此外,如果您在方法中使用 async-await,則傳回類型必須是 'Task' 或 'ValueTask'。 + + + + Test method {0}.{1} threw exception: +{2} + 測試方法 {0}.{1} 擲回例外狀況: +{2} + + + + Unable to get type {0}. Error: {1}. + 無法取得類型 {0}。錯誤: {1}。 + + + + The called code threw an exception that was caught, but the exception value was null + 被呼叫的程式碼擲回攔截到的例外狀況,但例外狀況值為 Null + + + + {0} For UWP projects, if you are using UI objects in test consider using [UITestMethod] attribute instead of [TestMethod] to execute test in UI thread. + {0} 針對 UWP 專案,如果您在測試中使用 UI 物件,請考慮使用 [UITestMethod] 屬性取代 [TestMethod] 在 UI 執行緒中執行測試。 + + + + (Failed to get the message for an exception of type {0} due to an exception.) + (因為發生例外狀況,所以無法取得類型 {0} 之例外狀況的訊息。) + + + + 'MSTest.TestAdapter' and 'MSTest.TestFramework' must have the same version. Found 'MSTest.TestAdapter' version '{0}' and 'MSTest.TestFramework' version '{1}'. Please make sure that the versions of 'MSTest.TestAdapter' and 'MSTest.TestFramework' NuGet packages have the same version. + 'MSTest.TestAdapter' 和 'MSTest.TestFramework' 必須使用相同的版本。發現 'MSTest.TestAdapter' 的版本為 '{0}',而 'MSTest.TestFramework' 的版本為 '{1}'。請確保 'MSTest.TestAdapter' 和 'MSTest.TestFramework' 的 NuGet 套件版本一致。 + + Wrong number of objects for permutation. Should be greater than zero. 排列的物件數目錯誤。應大於零。 diff --git a/src/Adapter/MSTest.TestAdapter/RunConfigurationSettings.cs b/src/Adapter/MSTestAdapter.PlatformServices/RunConfigurationSettings.cs similarity index 98% rename from src/Adapter/MSTest.TestAdapter/RunConfigurationSettings.cs rename to src/Adapter/MSTestAdapter.PlatformServices/RunConfigurationSettings.cs index b2c9b24b54..915bca4761 100644 --- a/src/Adapter/MSTest.TestAdapter/RunConfigurationSettings.cs +++ b/src/Adapter/MSTestAdapter.PlatformServices/RunConfigurationSettings.cs @@ -16,9 +16,9 @@ namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter; /// The run configuration settings. /// #if NET6_0_OR_GREATER -[Obsolete(Constants.PublicTypeObsoleteMessage, DiagnosticId = "MSTESTOBS")] +[Obsolete(FrameworkConstants.PublicTypeObsoleteMessage, DiagnosticId = "MSTESTOBS")] #else -[Obsolete(Constants.PublicTypeObsoleteMessage)] +[Obsolete(FrameworkConstants.PublicTypeObsoleteMessage)] #endif public class RunConfigurationSettings { diff --git a/src/Adapter/MSTestAdapter.PlatformServices/Services/FileOperations.cs b/src/Adapter/MSTestAdapter.PlatformServices/Services/FileOperations.cs index 9f5399670c..d62f517c60 100644 --- a/src/Adapter/MSTestAdapter.PlatformServices/Services/FileOperations.cs +++ b/src/Adapter/MSTestAdapter.PlatformServices/Services/FileOperations.cs @@ -16,9 +16,9 @@ namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices; /// The file operations. /// #if NET6_0_OR_GREATER -[Obsolete(Constants.PublicTypeObsoleteMessage, DiagnosticId = "MSTESTOBS")] +[Obsolete(TestTools.UnitTesting.FrameworkConstants.PublicTypeObsoleteMessage, DiagnosticId = "MSTESTOBS")] #else -[Obsolete(Constants.PublicTypeObsoleteMessage)] +[Obsolete(TestTools.UnitTesting.FrameworkConstants.PublicTypeObsoleteMessage)] #endif public class FileOperations : IFileOperations { @@ -45,7 +45,11 @@ public Assembly LoadAssembly(string assemblyName, bool isReflectionOnly) } #endif string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(assemblyName); - Assembly assembly = _assemblyCache.GetOrAdd(fileNameWithoutExtension, fileNameWithoutExtension => Assembly.Load(new AssemblyName(fileNameWithoutExtension))); + // Do NOT use `new AssemblyName(fileNameWithoutExtension)` here. The provided string is a FullName of assembly and needs to be properly escaped (but there is no utility for it). + // To correctly construct AssemblyName from file name, we need to just set the Name, and it will be escaped correct when constructing FullName. + // https://github.com/dotnet/runtime/blob/da322a2260bcb07347df3082fca211987ec8f2fc/src/libraries/Common/src/System/Reflection/AssemblyNameFormatter.cs#L120 + // When we did it wrong the exception thrown is "The given assembly name was invalid." for files that have `=` or any other special characters in their names (see the AssemblyNameFormatter.cs code). + Assembly assembly = _assemblyCache.GetOrAdd(fileNameWithoutExtension, fileNameWithoutExtension => Assembly.Load(new AssemblyName { Name = fileNameWithoutExtension })); return assembly; #elif NETFRAMEWORK diff --git a/src/Adapter/MSTestAdapter.PlatformServices/Services/MSTestAdapterSettings.cs b/src/Adapter/MSTestAdapter.PlatformServices/Services/MSTestAdapterSettings.cs index 8266bf58e7..8f4f179327 100644 --- a/src/Adapter/MSTestAdapter.PlatformServices/Services/MSTestAdapterSettings.cs +++ b/src/Adapter/MSTestAdapter.PlatformServices/Services/MSTestAdapterSettings.cs @@ -14,9 +14,9 @@ namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices; /// The MSTest settings. /// #if NET6_0_OR_GREATER -[Obsolete(Constants.PublicTypeObsoleteMessage, DiagnosticId = "MSTESTOBS")] +[Obsolete(TestTools.UnitTesting.FrameworkConstants.PublicTypeObsoleteMessage, DiagnosticId = "MSTESTOBS")] #else -[Obsolete(Constants.PublicTypeObsoleteMessage)] +[Obsolete(TestTools.UnitTesting.FrameworkConstants.PublicTypeObsoleteMessage)] #endif public class MSTestAdapterSettings { @@ -161,7 +161,7 @@ internal static MSTestAdapterSettings ToSettings(IConfiguration configuration) // }, // ... remaining settings // } - var settings = new MSTestAdapterSettings(); + MSTestAdapterSettings settings = MSTestSettingsProvider.Settings; Configuration = configuration; ParseBooleanSetting(configuration, "deployment:enabled", value => settings.DeploymentEnabled = value); ParseBooleanSetting(configuration, "deployment:deployTestSourceDependencies", value => settings.DeployTestSourceDependencies = value); diff --git a/src/Adapter/MSTestAdapter.PlatformServices/Services/ReflectionOperations.cs b/src/Adapter/MSTestAdapter.PlatformServices/Services/ReflectionOperations.cs index 2e37b5afba..cf49b2781d 100644 --- a/src/Adapter/MSTestAdapter.PlatformServices/Services/ReflectionOperations.cs +++ b/src/Adapter/MSTestAdapter.PlatformServices/Services/ReflectionOperations.cs @@ -2,6 +2,7 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface; +using Microsoft.VisualStudio.TestTools.UnitTesting; #if NETFRAMEWORK using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Utilities; #endif @@ -12,9 +13,9 @@ namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices; /// This service is responsible for platform specific reflection operations. /// #if NET6_0_OR_GREATER -[Obsolete(Constants.PublicTypeObsoleteMessage, DiagnosticId = "MSTESTOBS")] +[Obsolete(FrameworkConstants.PublicTypeObsoleteMessage, DiagnosticId = "MSTESTOBS")] #else -[Obsolete(Constants.PublicTypeObsoleteMessage)] +[Obsolete(FrameworkConstants.PublicTypeObsoleteMessage)] #endif public class ReflectionOperations : IReflectionOperations { @@ -27,7 +28,7 @@ public class ReflectionOperations : IReflectionOperations [return: NotNullIfNotNull(nameof(memberInfo))] public object[]? GetCustomAttributes(MemberInfo memberInfo, bool inherit) #if NETFRAMEWORK - => ReflectionUtility.GetCustomAttributes(memberInfo, inherit).ToArray(); + => [.. ReflectionUtility.GetCustomAttributes(memberInfo, inherit)]; #else { object[] attributes = memberInfo.GetCustomAttributes(typeof(Attribute), inherit); @@ -51,7 +52,7 @@ public class ReflectionOperations : IReflectionOperations [return: NotNullIfNotNull(nameof(memberInfo))] public object[]? GetCustomAttributes(MemberInfo memberInfo, Type type, bool inherit) => #if NETFRAMEWORK - ReflectionUtility.GetCustomAttributes(memberInfo, type, inherit).ToArray(); + [.. ReflectionUtility.GetCustomAttributes(memberInfo, type, inherit)]; #else memberInfo.GetCustomAttributes(type, inherit); #endif diff --git a/src/Adapter/MSTestAdapter.PlatformServices/Services/SettingsProvider.cs b/src/Adapter/MSTestAdapter.PlatformServices/Services/SettingsProvider.cs index 26e97bf5d5..c530d8d802 100644 --- a/src/Adapter/MSTestAdapter.PlatformServices/Services/SettingsProvider.cs +++ b/src/Adapter/MSTestAdapter.PlatformServices/Services/SettingsProvider.cs @@ -9,9 +9,9 @@ namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices; /// Class to read settings from the runsettings xml for the desktop. /// #if NET6_0_OR_GREATER -[Obsolete(Constants.PublicTypeObsoleteMessage, DiagnosticId = "MSTESTOBS")] +[Obsolete(TestTools.UnitTesting.FrameworkConstants.PublicTypeObsoleteMessage, DiagnosticId = "MSTESTOBS")] #else -[Obsolete(Constants.PublicTypeObsoleteMessage)] +[Obsolete(TestTools.UnitTesting.FrameworkConstants.PublicTypeObsoleteMessage)] #endif public class MSTestSettingsProvider : ISettingsProvider { @@ -37,9 +37,16 @@ public static MSTestAdapterSettings Settings internal static void Load(IConfiguration configuration) { #if !WINDOWS_UWP -#pragma warning disable IDE0022 // Use expression body for method var settings = MSTestAdapterSettings.ToSettings(configuration); -#pragma warning restore IDE0022 // Use expression body for method + if (!ReferenceEquals(settings, Settings)) + { + // NOTE: ToSettings mutates the Settings property and just returns it. + // This invariant is important to preserve, because we load from from runsettings through the XmlReader overload below. + // Then we read via IConfiguration. + // So this path should be unreachable. + // In v4 when we will make this class internal, we can start changing the API to clean this up. + throw ApplicationStateGuard.Unreachable(); + } #endif } @@ -51,7 +58,16 @@ public void Load(XmlReader reader) { #if !WINDOWS_UWP Guard.NotNull(reader); - Settings = MSTestAdapterSettings.ToSettings(reader); + var settings = MSTestAdapterSettings.ToSettings(reader); + if (!ReferenceEquals(settings, Settings)) + { + // NOTE: ToSettings mutates the Settings property and just returns it. + // This invariant is important to preserve, because we load from from runsettings through the XmlReader overload below. + // Then we read via IConfiguration. + // So this path should be unreachable. + // In v4 when we will make this class internal, we can start changing the API to clean this up. + throw ApplicationStateGuard.Unreachable(); + } #endif } diff --git a/src/Adapter/MSTestAdapter.PlatformServices/Services/TestContextImplementation.cs b/src/Adapter/MSTestAdapter.PlatformServices/Services/TestContextImplementation.cs index c129f79d52..44eb70f27c 100644 --- a/src/Adapter/MSTestAdapter.PlatformServices/Services/TestContextImplementation.cs +++ b/src/Adapter/MSTestAdapter.PlatformServices/Services/TestContextImplementation.cs @@ -6,6 +6,7 @@ using System.Data.Common; #endif +using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter; using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface; using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging; using Microsoft.VisualStudio.TestTools.UnitTesting; @@ -20,22 +21,46 @@ namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices; /// like GetProperty<string>("TestName") or GetProperty<string>("FullyQualifiedTestClassName"). /// #if NET6_0_OR_GREATER -[Obsolete(Constants.PublicTypeObsoleteMessage, DiagnosticId = "MSTESTOBS")] +[Obsolete(FrameworkConstants.PublicTypeObsoleteMessage, DiagnosticId = "MSTESTOBS")] #else -[Obsolete(Constants.PublicTypeObsoleteMessage)] +[Obsolete(FrameworkConstants.PublicTypeObsoleteMessage)] #endif -public class TestContextImplementation : TestContext, ITestContext +public sealed class TestContextImplementation : TestContext, ITestContext, IDisposable { - /// - /// List of result files associated with the test. - /// - private readonly List _testResultFiles; + internal sealed class SynchronizedStringBuilder + { + private readonly StringBuilder _builder = new(); - /// - /// Writer on which the messages given by the user should be written. - /// - private readonly StringWriter _stringWriter; - private readonly ThreadSafeStringWriter? _threadSafeStringWriter; + [MethodImpl(MethodImplOptions.Synchronized)] + internal void Append(char value) + => _builder.Append(value); + + [MethodImpl(MethodImplOptions.Synchronized)] + internal void Append(string? value) + => _builder.Append(value); + + [MethodImpl(MethodImplOptions.Synchronized)] + internal void Append(char[] buffer, int index, int count) + => _builder.Append(buffer, index, count); + + [MethodImpl(MethodImplOptions.Synchronized)] + internal void AppendLine(string? value) + => _builder.AppendLine(value); + + [MethodImpl(MethodImplOptions.Synchronized)] + internal void Clear() + => _builder.Clear(); + + [MethodImpl(MethodImplOptions.Synchronized)] + public override string ToString() + => _builder.ToString(); + } + + private static readonly AsyncLocal CurrentTestContextAsyncLocal = new(); + + // This should be removed. Don't rely on it. + // We only keep it for public constructor, but we don't pass it by the adapter. + private readonly StringWriter? _stringWriter; /// /// Properties. @@ -43,10 +68,17 @@ public class TestContextImplementation : TestContext, ITestContext private readonly Dictionary _properties; private readonly IMessageLogger? _messageLogger; + private CancellationTokenRegistration? _cancellationTokenRegistration; + /// - /// Specifies whether the writer is disposed or not. + /// List of result files associated with the test. /// - private bool _stringWriterDisposed; + private List? _testResultFiles; + + private SynchronizedStringBuilder? _stdOutStringBuilder; + private SynchronizedStringBuilder? _stdErrStringBuilder; + private SynchronizedStringBuilder? _traceStringBuilder; + private SynchronizedStringBuilder? _testContextMessageStringBuilder; /// /// Unit test outcome. @@ -65,16 +97,21 @@ public class TestContextImplementation : TestContext, ITestContext private DataRow? _dataRow; #endif + private static readonly Action CancelDelegate = static state => ((TestContextImplementation)state!).Context.CancellationTokenSource.Cancel(); + /// /// Initializes a new instance of the class. /// /// The test method. - /// The writer where diagnostic messages are written to. /// Properties/configuration passed in. /// The message logger to use. - internal TestContextImplementation(ITestMethod? testMethod, StringWriter stringWriter, IDictionary properties, IMessageLogger messageLogger) - : this(testMethod, stringWriter, properties) - => _messageLogger = messageLogger; + /// The global test run cancellation token. + internal TestContextImplementation(ITestMethod? testMethod, IDictionary properties, IMessageLogger messageLogger, TestRunCancellationToken? testRunCancellationToken) + : this(testMethod, null!, properties) + { + _messageLogger = messageLogger; + _cancellationTokenRegistration = testRunCancellationToken?.Register(CancelDelegate, this); + } /// /// Initializes a new instance of the class. @@ -87,27 +124,28 @@ public TestContextImplementation(ITestMethod? testMethod, StringWriter stringWri // testMethod can be null when running ForceCleanup (done when reaching --maximum-failed-tests. DebugEx.Assert(properties != null, "properties is not null"); -#if NETFRAMEWORK - DebugEx.Assert(stringWriter != null, "StringWriter is not null"); -#endif - _stringWriter = stringWriter; - - // Cannot get this type in constructor directly, because all signatures for all platforms need to be the same. - _threadSafeStringWriter = stringWriter as ThreadSafeStringWriter; - _properties = testMethod is null - ? new Dictionary(properties) - : new Dictionary(properties) + if (testMethod is null) + { + _properties = new Dictionary(properties); + } + else + { + _properties = new Dictionary(properties.Count + 4); + foreach (KeyValuePair kvp in properties) { - [FullyQualifiedTestClassNameLabel] = testMethod.FullClassName, - [ManagedTypeLabel] = testMethod.ManagedTypeName, - [ManagedMethodLabel] = testMethod.ManagedMethodName, - [TestNameLabel] = testMethod.Name, - }; + _properties[kvp.Key] = kvp.Value; + } - _testResultFiles = []; + _properties[FullyQualifiedTestClassNameLabel] = testMethod.FullClassName; + _properties[ManagedTypeLabel] = testMethod.ManagedTypeName; + _properties[ManagedMethodLabel] = testMethod.ManagedMethodName; + _properties[TestNameLabel] = testMethod.Name; + } } + internal static TestContextImplementation? CurrentTestContext => CurrentTestContextAsyncLocal.Value; + #region TestContext impl /// @@ -168,7 +206,7 @@ public override void AddResultFile(string fileName) throw new ArgumentException(Resource.Common_CannotBeNullOrEmpty, nameof(fileName)); } - _testResultFiles.Add(Path.GetFullPath(fileName)); + (_testResultFiles ??= []).Add(Path.GetFullPath(fileName)); } /// @@ -178,20 +216,9 @@ public override void AddResultFile(string fileName) /// The formatted string that contains the trace message. public override void Write(string? message) { - if (_stringWriterDisposed) - { - return; - } - - try - { - string? msg = message?.Replace("\0", "\\0"); - _stringWriter.Write(msg); - } - catch (ObjectDisposedException) - { - _stringWriterDisposed = true; - } + string? msg = message?.Replace("\0", "\\0"); + GetTestContextMessagesStringBuilder().Append(msg); + _stringWriter?.Write(msg); } /// @@ -202,20 +229,9 @@ public override void Write(string? message) /// Arguments to add to the trace message. public override void Write(string format, params object?[] args) { - if (_stringWriterDisposed) - { - return; - } - - try - { - string message = string.Format(CultureInfo.CurrentCulture, format.Replace("\0", "\\0"), args); - _stringWriter.Write(message); - } - catch (ObjectDisposedException) - { - _stringWriterDisposed = true; - } + string message = string.Format(CultureInfo.CurrentCulture, format.Replace("\0", "\\0"), args); + GetTestContextMessagesStringBuilder().Append(message); + _stringWriter?.Write(message); } /// @@ -225,20 +241,9 @@ public override void Write(string format, params object?[] args) /// The formatted string that contains the trace message. public override void WriteLine(string? message) { - if (_stringWriterDisposed) - { - return; - } - - try - { - string? msg = message?.Replace("\0", "\\0"); - _stringWriter.WriteLine(msg); - } - catch (ObjectDisposedException) - { - _stringWriterDisposed = true; - } + string? msg = message?.Replace("\0", "\\0"); + GetTestContextMessagesStringBuilder().AppendLine(msg); + _stringWriter?.Write(msg); } /// @@ -249,20 +254,9 @@ public override void WriteLine(string? message) /// Arguments to add to the trace message. public override void WriteLine(string format, params object?[] args) { - if (_stringWriterDisposed) - { - return; - } - - try - { - string message = string.Format(CultureInfo.CurrentCulture, format.Replace("\0", "\\0"), args); - _stringWriter.WriteLine(message); - } - catch (ObjectDisposedException) - { - _stringWriterDisposed = true; - } + string message = string.Format(CultureInfo.CurrentCulture, format.Replace("\0", "\\0"), args); + GetTestContextMessagesStringBuilder().AppendLine(message); + _stringWriter?.Write(message); } /// @@ -336,7 +330,7 @@ public void AddProperty(string propertyName, string propertyValue) /// Results files generated in run. public IList? GetResultFiles() { - if (_testResultFiles.Count == 0) + if (_testResultFiles is null || _testResultFiles.Count == 0) { return null; } @@ -354,13 +348,16 @@ public void AddProperty(string propertyName, string propertyValue) /// /// The test context messages added so far. public string? GetDiagnosticMessages() - => _stringWriter.ToString(); + => _testContextMessageStringBuilder?.ToString(); /// /// Clears the previous testContext writeline messages. /// public void ClearDiagnosticMessages() - => _threadSafeStringWriter?.ToStringAndClear(); + { + _testContextMessageStringBuilder?.Clear(); + (_stringWriter as ThreadSafeStringWriter)?.ToStringAndClear(); + } /// public void SetDisplayName(string? displayName) @@ -370,4 +367,86 @@ public void SetDisplayName(string? displayName) public override void DisplayMessage(MessageLevel messageLevel, string message) => _messageLogger?.SendMessage(messageLevel.ToTestMessageLevel(), message); #endregion + + /// + public void Dispose() + { + _cancellationTokenRegistration?.Dispose(); + _cancellationTokenRegistration = null; + } + + internal readonly struct ScopedTestContextSetter : IDisposable + { + internal ScopedTestContextSetter(TestContextImplementation? testContext) + => CurrentTestContextAsyncLocal.Value = testContext; + + public void Dispose() + => CurrentTestContextAsyncLocal.Value = null; + } + + internal static ScopedTestContextSetter SetCurrentTestContext(TestContextImplementation? testContext) + => new(testContext); + + internal void WriteConsoleOut(char value) + => GetOutStringBuilder().Append(value); + + internal void WriteConsoleOut(string? value) + => GetOutStringBuilder().Append(value); + + internal void WriteConsoleOut(char[] buffer, int index, int count) + => GetOutStringBuilder().Append(buffer, index, count); + + internal void WriteConsoleErr(char value) + => GetErrStringBuilder().Append(value); + + internal void WriteConsoleErr(string? value) + => GetErrStringBuilder().Append(value); + + internal void WriteConsoleErr(char[] buffer, int index, int count) + => GetErrStringBuilder().Append(buffer, index, count); + + internal void WriteTrace(char value) + => GetTraceStringBuilder().Append(value); + + internal void WriteTrace(string? value) + => GetTraceStringBuilder().Append(value); + + private SynchronizedStringBuilder GetOutStringBuilder() + { + _ = _stdOutStringBuilder ?? Interlocked.CompareExchange(ref _stdOutStringBuilder, new SynchronizedStringBuilder(), null)!; + return _stdOutStringBuilder; + } + + private SynchronizedStringBuilder GetErrStringBuilder() + { + _ = _stdErrStringBuilder ?? Interlocked.CompareExchange(ref _stdErrStringBuilder, new SynchronizedStringBuilder(), null)!; + return _stdErrStringBuilder; + } + + private SynchronizedStringBuilder GetTraceStringBuilder() + { + _ = _traceStringBuilder ?? Interlocked.CompareExchange(ref _traceStringBuilder, new SynchronizedStringBuilder(), null)!; + return _traceStringBuilder; + } + + private SynchronizedStringBuilder GetTestContextMessagesStringBuilder() + { + // Prefer writing to the current test context instead of 'this'. + // This is just a hack to preserve backward compatibility. + // It's relevant for cases where users store 'TestContext' in a static field. + // Then, they write to the "wrong" test context. + // Here, we are correcting user's fault by finding out the correct TestContext that should receive the message. + TestContextImplementation @this = CurrentTestContext ?? this; + _ = @this._testContextMessageStringBuilder ?? Interlocked.CompareExchange(ref @this._testContextMessageStringBuilder, new SynchronizedStringBuilder(), null)!; + return @this._testContextMessageStringBuilder; + } + + internal string? GetOut() + => _stdOutStringBuilder?.ToString(); + + internal string? GetErr() + => _stdErrStringBuilder?.ToString(); + + internal string? GetTrace() + => _traceStringBuilder?.ToString(); } diff --git a/src/Adapter/MSTestAdapter.PlatformServices/Services/TestDataSource.cs b/src/Adapter/MSTestAdapter.PlatformServices/Services/TestDataSource.cs index 4f1a4f90d9..328576496c 100644 --- a/src/Adapter/MSTestAdapter.PlatformServices/Services/TestDataSource.cs +++ b/src/Adapter/MSTestAdapter.PlatformServices/Services/TestDataSource.cs @@ -24,9 +24,9 @@ namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices; /// a ReflectionTypeLoadException. /// #if NET6_0_OR_GREATER -[Obsolete(Constants.PublicTypeObsoleteMessage, DiagnosticId = "MSTESTOBS")] +[Obsolete(TestTools.UnitTesting.FrameworkConstants.PublicTypeObsoleteMessage, DiagnosticId = "MSTESTOBS")] #else -[Obsolete(Constants.PublicTypeObsoleteMessage)] +[Obsolete(TestTools.UnitTesting.FrameworkConstants.PublicTypeObsoleteMessage)] #endif public class TestDataSource : ITestDataSource { @@ -147,7 +147,7 @@ private static void GetConnectionProperties(DataSourceAttribute dataSourceAttrib providerNameInvariant = ConfigurationManager.ConnectionStrings[element.ConnectionString].ProviderName; connectionString = ConfigurationManager.ConnectionStrings[element.ConnectionString].ConnectionString; tableName = element.DataTableName; - dataAccessMethod = EnumPolyfill.Parse(element.DataAccessMethod); + dataAccessMethod = Enum.Parse(element.DataAccessMethod); } #endif } diff --git a/src/Adapter/MSTestAdapter.PlatformServices/Services/TestDeployment.cs b/src/Adapter/MSTestAdapter.PlatformServices/Services/TestDeployment.cs index 03a50cdfa3..bb745acf5b 100644 --- a/src/Adapter/MSTestAdapter.PlatformServices/Services/TestDeployment.cs +++ b/src/Adapter/MSTestAdapter.PlatformServices/Services/TestDeployment.cs @@ -23,9 +23,9 @@ namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices; /// The test deployment. /// #if NET6_0_OR_GREATER -[Obsolete(Constants.PublicTypeObsoleteMessage, DiagnosticId = "MSTESTOBS")] +[Obsolete(TestTools.UnitTesting.FrameworkConstants.PublicTypeObsoleteMessage, DiagnosticId = "MSTESTOBS")] #else -[Obsolete(Constants.PublicTypeObsoleteMessage)] +[Obsolete(TestTools.UnitTesting.FrameworkConstants.PublicTypeObsoleteMessage)] #endif public class TestDeployment : ITestDeployment { diff --git a/src/Adapter/MSTestAdapter.PlatformServices/Services/TestSource.cs b/src/Adapter/MSTestAdapter.PlatformServices/Services/TestSource.cs index c0cbeab9cd..b93d0aaa05 100644 --- a/src/Adapter/MSTestAdapter.PlatformServices/Services/TestSource.cs +++ b/src/Adapter/MSTestAdapter.PlatformServices/Services/TestSource.cs @@ -5,6 +5,7 @@ using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.AppContainer; #endif using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface; +using Microsoft.VisualStudio.TestTools.UnitTesting; #if NETFRAMEWORK using Microsoft.VisualStudio.TestPlatform.ObjectModel.Utilities; #endif @@ -16,9 +17,9 @@ namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices; /// the test sources provided to the adapter. /// #if NET6_0_OR_GREATER -[Obsolete(Constants.PublicTypeObsoleteMessage, DiagnosticId = "MSTESTOBS")] +[Obsolete(FrameworkConstants.PublicTypeObsoleteMessage, DiagnosticId = "MSTESTOBS")] #else -[Obsolete(Constants.PublicTypeObsoleteMessage)] +[Obsolete(FrameworkConstants.PublicTypeObsoleteMessage)] #endif public class TestSource : ITestSource { @@ -27,22 +28,22 @@ public class TestSource : ITestSource private static readonly IEnumerable ExecutableExtensions = new HashSet() { - Constants.ExeExtension, + EngineConstants.ExeExtension, // Required only for store 8.1. In future if that support is needed, uncomment this. // Constants.DllExtension }; - private static readonly HashSet SystemAssemblies = new(new string[] - { + private static readonly HashSet SystemAssemblies = + [ "MICROSOFT.CSHARP.DLL", "MICROSOFT.VISUALBASIC.DLL", "CLRCOMPRESSION.DLL", - }); + ]; // Well known platform assemblies. - private static readonly HashSet PlatformAssemblies = new(new string[] - { + private static readonly HashSet PlatformAssemblies = + [ "MICROSOFT.VISUALSTUDIO.TESTPLATFORM.TESTFRAMEWORK.DLL", "MICROSOFT.VISUALSTUDIO.TESTPLATFORM.TESTFRAMEWORK.EXTENSIONS.CORE.DLL", "MICROSOFT.VISUALSTUDIO.TESTPLATFORM.CORE.DLL", @@ -54,7 +55,7 @@ public class TestSource : ITestSource "VSTEST_EXECUTIONENGINE_PLATFORMBRIDGE.DLL", "VSTEST_EXECUTIONENGINE_PLATFORMBRIDGE.WINMD", "VSTEST.EXECUTIONENGINE.WINDOWSPHONE.DLL", - }); + ]; #endif /// @@ -62,13 +63,13 @@ public class TestSource : ITestSource /// public IEnumerable ValidSourceExtensions => new List { - Constants.DllExtension, + EngineConstants.DllExtension, #if NETFRAMEWORK - Constants.PhoneAppxPackageExtension, + EngineConstants.PhoneAppxPackageExtension, #elif WINDOWS_UWP || WIN_UI - Constants.AppxPackageExtension, + EngineConstants.AppxPackageExtension, #endif - Constants.ExeExtension, + EngineConstants.ExeExtension, }; /// @@ -149,7 +150,7 @@ public IEnumerable GetTestSources(IEnumerable sources) { // WinUI Desktop uses .NET 6, which builds both a .dll and an .exe. // The manifest will provide the .exe, but the tests are inside the .dll, so we replace the name here. - newSources.Add(Path.Combine(appxSourceDirectory, Path.ChangeExtension(filePath, Constants.DllExtension))); + newSources.Add(Path.Combine(appxSourceDirectory, Path.ChangeExtension(filePath, EngineConstants.DllExtension))); } } @@ -170,7 +171,7 @@ private static bool ContainsAppxSource(IEnumerable sources) { foreach (string source in sources) { - if (string.Equals(Path.GetExtension(source), Constants.AppxPackageExtension, StringComparison.OrdinalIgnoreCase)) + if (string.Equals(Path.GetExtension(source), EngineConstants.AppxPackageExtension, StringComparison.OrdinalIgnoreCase)) { return true; } @@ -190,7 +191,7 @@ private static bool ContainsAppxSource(IEnumerable sources) { foreach (string source in sources) { - if (string.Equals(Path.GetExtension(source), Constants.AppxPackageExtension, StringComparison.OrdinalIgnoreCase)) + if (string.Equals(Path.GetExtension(source), EngineConstants.AppxPackageExtension, StringComparison.OrdinalIgnoreCase)) { return source; } diff --git a/src/Adapter/MSTestAdapter.PlatformServices/Services/TestSourceHost.cs b/src/Adapter/MSTestAdapter.PlatformServices/Services/TestSourceHost.cs index 54f530d721..a88a8d463e 100644 --- a/src/Adapter/MSTestAdapter.PlatformServices/Services/TestSourceHost.cs +++ b/src/Adapter/MSTestAdapter.PlatformServices/Services/TestSourceHost.cs @@ -22,9 +22,9 @@ namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices; /// A host that loads the test source. This can be in isolation for desktop using an AppDomain or just loading the source in the current context. /// #if NET6_0_OR_GREATER -[Obsolete(Constants.PublicTypeObsoleteMessage, DiagnosticId = "MSTESTOBS")] +[Obsolete(TestTools.UnitTesting.FrameworkConstants.PublicTypeObsoleteMessage, DiagnosticId = "MSTESTOBS")] #else -[Obsolete(Constants.PublicTypeObsoleteMessage)] +[Obsolete(TestTools.UnitTesting.FrameworkConstants.PublicTypeObsoleteMessage)] #endif public class TestSourceHost : ITestSourceHost { @@ -120,7 +120,7 @@ public void SetupHost() if (EqtTrace.IsInfoEnabled) { - EqtTrace.Info("DesktopTestSourceHost.SetupHost(): Creating assembly resolver with resolution paths {0}.", string.Join(",", resolutionPaths)); + EqtTrace.Info("DesktopTestSourceHost.SetupHost(): Creating assembly resolver with resolution paths {0}.", string.Join(',', resolutionPaths)); } var assemblyResolver = new AssemblyResolver(resolutionPaths); @@ -137,7 +137,7 @@ public void SetupHost() if (EqtTrace.IsInfoEnabled) { - EqtTrace.Info("DesktopTestSourceHost.SetupHost(): Creating assembly resolver with resolution paths {0}.", string.Join(",", resolutionPaths)); + 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 @@ -221,19 +221,13 @@ public void SetupHost() public void Dispose() { #if NETFRAMEWORK || (NET && !WINDOWS_UWP) - if (_parentDomainAssemblyResolver != null) - { - _parentDomainAssemblyResolver.Dispose(); - _parentDomainAssemblyResolver = null; - } + _parentDomainAssemblyResolver?.Dispose(); + _parentDomainAssemblyResolver = null; #endif #if NETFRAMEWORK - if (_childDomainAssemblyResolver != null) - { - _childDomainAssemblyResolver.Dispose(); - _childDomainAssemblyResolver = null; - } + _childDomainAssemblyResolver?.Dispose(); + _childDomainAssemblyResolver = null; if (AppDomain != null) { @@ -346,7 +340,7 @@ internal string GetAppBaseAsPerPlatform() // UWP platform service assembly at the test source location and since CLR starts looking for assemblies from the app base location, // there would be a mismatch of platform service assemblies during discovery. DebugEx.Assert(_targetFrameworkVersion is not null, "Target framework version is null."); - return _targetFrameworkVersion.Contains(Constants.DotNetFrameWorkStringPrefix) + return _targetFrameworkVersion.Contains(EngineConstants.DotNetFrameWorkStringPrefix) ? Path.GetDirectoryName(_sourceFileName) ?? Path.GetDirectoryName(typeof(TestSourceHost).Assembly.Location) : Path.GetDirectoryName(typeof(TestSourceHost).Assembly.Location); } @@ -428,10 +422,6 @@ private static bool TryAddSearchDirectoriesSpecifiedInRunSettingsToAssemblyResol { // Check if user specified any adapter settings MSTestAdapterSettings adapterSettings = MSTestSettingsProvider.Settings; - if (adapterSettings == null) - { - return false; - } try { diff --git a/src/Adapter/MSTestAdapter.PlatformServices/Services/ThreadOperations.cs b/src/Adapter/MSTestAdapter.PlatformServices/Services/ThreadOperations.cs index 294ec53bfd..756793e443 100644 --- a/src/Adapter/MSTestAdapter.PlatformServices/Services/ThreadOperations.cs +++ b/src/Adapter/MSTestAdapter.PlatformServices/Services/ThreadOperations.cs @@ -10,9 +10,9 @@ namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices; /// This service is responsible for any Async operations specific to a platform. /// #if NET6_0_OR_GREATER -[Obsolete(Constants.PublicTypeObsoleteMessage, DiagnosticId = "MSTESTOBS")] +[Obsolete(TestTools.UnitTesting.FrameworkConstants.PublicTypeObsoleteMessage, DiagnosticId = "MSTESTOBS")] #else -[Obsolete(Constants.PublicTypeObsoleteMessage)] +[Obsolete(TestTools.UnitTesting.FrameworkConstants.PublicTypeObsoleteMessage)] #endif public class ThreadOperations : IThreadOperations { diff --git a/src/Adapter/MSTestAdapter.PlatformServices/Services/ThreadSafeStringWriter.cs b/src/Adapter/MSTestAdapter.PlatformServices/Services/ThreadSafeStringWriter.cs index 821e25829d..3859b40cd8 100644 --- a/src/Adapter/MSTestAdapter.PlatformServices/Services/ThreadSafeStringWriter.cs +++ b/src/Adapter/MSTestAdapter.PlatformServices/Services/ThreadSafeStringWriter.cs @@ -7,9 +7,9 @@ namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices; /// AsyncContext aware, thread safe string writer that allows output writes from different threads to end up in the same async local context. /// #if NET6_0_OR_GREATER -[Obsolete(Constants.PublicTypeObsoleteMessage, DiagnosticId = "MSTESTOBS")] +[Obsolete(TestTools.UnitTesting.FrameworkConstants.PublicTypeObsoleteMessage, DiagnosticId = "MSTESTOBS")] #else -[Obsolete(Constants.PublicTypeObsoleteMessage)] +[Obsolete(TestTools.UnitTesting.FrameworkConstants.PublicTypeObsoleteMessage)] #endif public class ThreadSafeStringWriter : StringWriter { @@ -183,7 +183,7 @@ internal static void CleanState() { lock (StaticLockObject) { - State.Value = new(); + State.Value = []; } } diff --git a/src/Adapter/MSTestAdapter.PlatformServices/Services/TraceListener.cs b/src/Adapter/MSTestAdapter.PlatformServices/Services/TraceListener.cs index 5308c23d72..485b351528 100644 --- a/src/Adapter/MSTestAdapter.PlatformServices/Services/TraceListener.cs +++ b/src/Adapter/MSTestAdapter.PlatformServices/Services/TraceListener.cs @@ -13,9 +13,9 @@ namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices; /// like Close(), Dispose() etc. /// #if NET6_0_OR_GREATER -[Obsolete(Constants.PublicTypeObsoleteMessage, DiagnosticId = "MSTESTOBS")] +[Obsolete(TestTools.UnitTesting.FrameworkConstants.PublicTypeObsoleteMessage, DiagnosticId = "MSTESTOBS")] #else -[Obsolete(Constants.PublicTypeObsoleteMessage)] +[Obsolete(TestTools.UnitTesting.FrameworkConstants.PublicTypeObsoleteMessage)] #endif public class TraceListenerWrapper : #if !WINDOWS_UWP && !WIN_UI diff --git a/src/Adapter/MSTestAdapter.PlatformServices/Services/TraceListenerManager.cs b/src/Adapter/MSTestAdapter.PlatformServices/Services/TraceListenerManager.cs index e811d1cd15..1e427fc766 100644 --- a/src/Adapter/MSTestAdapter.PlatformServices/Services/TraceListenerManager.cs +++ b/src/Adapter/MSTestAdapter.PlatformServices/Services/TraceListenerManager.cs @@ -10,9 +10,9 @@ namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices; /// Responsible for performing Add(), Remove(), Close(), Dispose() operations on traceListener object. /// #if NET6_0_OR_GREATER -[Obsolete(Constants.PublicTypeObsoleteMessage, DiagnosticId = "MSTESTOBS")] +[Obsolete(TestTools.UnitTesting.FrameworkConstants.PublicTypeObsoleteMessage, DiagnosticId = "MSTESTOBS")] #else -[Obsolete(Constants.PublicTypeObsoleteMessage)] +[Obsolete(TestTools.UnitTesting.FrameworkConstants.PublicTypeObsoleteMessage)] #endif public class TraceListenerManager : ITraceListenerManager { diff --git a/src/Adapter/MSTestAdapter.PlatformServices/Services/TraceLogger.cs b/src/Adapter/MSTestAdapter.PlatformServices/Services/TraceLogger.cs index 44baf76b71..982509eaef 100644 --- a/src/Adapter/MSTestAdapter.PlatformServices/Services/TraceLogger.cs +++ b/src/Adapter/MSTestAdapter.PlatformServices/Services/TraceLogger.cs @@ -10,9 +10,9 @@ namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices; /// A service to log any trace messages from the adapter that would be shown in *.TpTrace files. /// #if NET6_0_OR_GREATER -[Obsolete(Constants.PublicTypeObsoleteMessage, DiagnosticId = "MSTESTOBS")] +[Obsolete(TestTools.UnitTesting.FrameworkConstants.PublicTypeObsoleteMessage, DiagnosticId = "MSTESTOBS")] #else -[Obsolete(Constants.PublicTypeObsoleteMessage)] +[Obsolete(TestTools.UnitTesting.FrameworkConstants.PublicTypeObsoleteMessage)] #endif public class AdapterTraceLogger : IAdapterTraceLogger { diff --git a/src/Adapter/MSTest.TestAdapter/TestMethodFilter.cs b/src/Adapter/MSTestAdapter.PlatformServices/TestMethodFilter.cs similarity index 92% rename from src/Adapter/MSTest.TestAdapter/TestMethodFilter.cs rename to src/Adapter/MSTestAdapter.PlatformServices/TestMethodFilter.cs index a713f7acc8..33f554a96c 100644 --- a/src/Adapter/MSTest.TestAdapter/TestMethodFilter.cs +++ b/src/Adapter/MSTestAdapter.PlatformServices/TestMethodFilter.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 Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices; using Microsoft.VisualStudio.TestPlatform.ObjectModel; using Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter; using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging; @@ -17,12 +18,12 @@ internal sealed class TestMethodFilter internal TestMethodFilter() => _supportedProperties = new Dictionary(StringComparer.OrdinalIgnoreCase) { - [Constants.TestCategoryProperty.Label] = Constants.TestCategoryProperty, - [Constants.PriorityProperty.Label] = Constants.PriorityProperty, + [EngineConstants.TestCategoryProperty.Label] = EngineConstants.TestCategoryProperty, + [EngineConstants.PriorityProperty.Label] = EngineConstants.PriorityProperty, [TestCaseProperties.FullyQualifiedName.Label] = TestCaseProperties.FullyQualifiedName, [TestCaseProperties.DisplayName.Label] = TestCaseProperties.DisplayName, [TestCaseProperties.Id.Label] = TestCaseProperties.Id, - [Constants.TestClassNameProperty.Label] = Constants.TestClassNameProperty, + [EngineConstants.TestClassNameProperty.Label] = EngineConstants.TestClassNameProperty, }; /// @@ -124,15 +125,14 @@ internal TestProperty PropertyProvider(string propertyName) MethodInfo? methodGetTestCaseFilter = context.GetType().GetRuntimeMethod("GetTestCaseFilter", [typeof(IEnumerable), typeof(Func)]); return (ITestCaseFilterExpression?)methodGetTestCaseFilter?.Invoke(context, [_supportedProperties.Keys, (Func)PropertyProvider]); } - catch (Exception ex) + catch (TargetInvocationException ex) { // In case of UWP .Net Native Tool Chain compilation. Invoking methods via Reflection doesn't work, hence discovery always fails. // Hence throwing exception only if it is of type TargetInvocationException(i.e. Method got invoked but something went wrong in GetTestCaseFilter Method) - if (ex is TargetInvocationException) - { - throw ex.InnerException!; - } - + throw ex.InnerException!; + } + catch (Exception ex) + { logger.SendMessage(TestMessageLevel.Warning, ex.Message); } diff --git a/src/Adapter/MSTestAdapter.PlatformServices/Utilities/AppDomainUtilities.cs b/src/Adapter/MSTestAdapter.PlatformServices/Utilities/AppDomainUtilities.cs index a80126e0b1..a4151b99a3 100644 --- a/src/Adapter/MSTestAdapter.PlatformServices/Utilities/AppDomainUtilities.cs +++ b/src/Adapter/MSTestAdapter.PlatformServices/Utilities/AppDomainUtilities.cs @@ -42,7 +42,7 @@ internal static void SetAppDomainFrameworkVersionBasedOnTestSource(AppDomainSetu { if (GetTargetFrameworkVersionFromVersionString(frameworkVersionString).CompareTo(Version45) > 0) { - PropertyInfo? pInfo = typeof(AppDomainSetup).GetProperty(Constants.TargetFrameworkName); + PropertyInfo? pInfo = typeof(AppDomainSetup).GetProperty(EngineConstants.TargetFrameworkName); pInfo?.SetValue(setup, frameworkVersionString, null); } } @@ -227,9 +227,9 @@ internal static Version GetTargetFrameworkVersionFromVersionString(string versio { try { - if (version.Length > Constants.DotNetFrameWorkStringPrefix.Length + 1) + if (version.Length > EngineConstants.DotNetFrameWorkStringPrefix.Length + 1) { - string versionPart = version.Substring(Constants.DotNetFrameWorkStringPrefix.Length + 1); + string versionPart = version.Substring(EngineConstants.DotNetFrameWorkStringPrefix.Length + 1); return new Version(versionPart); } } diff --git a/src/Adapter/MSTestAdapter.PlatformServices/Utilities/DeploymentItemUtility.cs b/src/Adapter/MSTestAdapter.PlatformServices/Utilities/DeploymentItemUtility.cs index bd8c0d78e6..892345da66 100644 --- a/src/Adapter/MSTestAdapter.PlatformServices/Utilities/DeploymentItemUtility.cs +++ b/src/Adapter/MSTestAdapter.PlatformServices/Utilities/DeploymentItemUtility.cs @@ -212,7 +212,7 @@ private static List GetDeploymentItems(IEnumerable deploymentIte return deploymentItemList1; } - IList result = new List(deploymentItemList1); + IList result = [.. deploymentItemList1]; foreach (DeploymentItem item in deploymentItemList2) { @@ -227,7 +227,7 @@ private static List GetDeploymentItems(IEnumerable deploymentIte /// /// The test Case. /// The . - private static KeyValuePair[]? GetDeploymentItems(TestCase testCase) => testCase.GetPropertyValue(Constants.DeploymentItemsProperty) as + private static KeyValuePair[]? GetDeploymentItems(TestCase testCase) => testCase.GetPropertyValue(EngineConstants.DeploymentItemsProperty) as KeyValuePair[]; private static KeyValuePair[]? ToKeyValuePairs(IEnumerable deploymentItemList) @@ -247,7 +247,7 @@ private static List GetDeploymentItems(IEnumerable deploymentIte } } - return result.ToArray(); + return [.. result]; } private static IList? FromKeyValuePairs(KeyValuePair[] deploymentItemsData) @@ -257,7 +257,7 @@ private static List GetDeploymentItems(IEnumerable deploymentIte return null; } - IList result = new List(); + IList result = []; foreach ((string? key, string? value) in deploymentItemsData) { diff --git a/src/Adapter/MSTestAdapter.PlatformServices/Utilities/DeploymentUtility.cs b/src/Adapter/MSTestAdapter.PlatformServices/Utilities/DeploymentUtility.cs index af43490bde..e7f32535b3 100644 --- a/src/Adapter/MSTestAdapter.PlatformServices/Utilities/DeploymentUtility.cs +++ b/src/Adapter/MSTestAdapter.PlatformServices/Utilities/DeploymentUtility.cs @@ -62,11 +62,7 @@ public override void AddDeploymentItemsBasedOnMsTestSetting(string testSource, I /// Root deployment directory. public override string GetRootDeploymentDirectory(string baseDirectory) { -#if NETFRAMEWORK || NETSTANDARD || NETCOREAPP3_1 - string dateTimeSuffix = $"{DateTime.Now.ToString("yyyyMMddTHHmmss", DateTimeFormatInfo.InvariantInfo)}_{Process.GetCurrentProcess().Id}"; -#else string dateTimeSuffix = $"{DateTime.Now.ToString("yyyyMMddTHHmmss", DateTimeFormatInfo.InvariantInfo)}_{Environment.ProcessId}"; -#endif string directoryName = string.Format(CultureInfo.InvariantCulture, Resource.TestRunName, DeploymentFolderPrefix, #if NETFRAMEWORK Environment.UserName, diff --git a/src/Adapter/MSTestAdapter.PlatformServices/Utilities/DeploymentUtilityBase.cs b/src/Adapter/MSTestAdapter.PlatformServices/Utilities/DeploymentUtilityBase.cs index 21de3f1a79..7b2f77a9a6 100644 --- a/src/Adapter/MSTestAdapter.PlatformServices/Utilities/DeploymentUtilityBase.cs +++ b/src/Adapter/MSTestAdapter.PlatformServices/Utilities/DeploymentUtilityBase.cs @@ -407,7 +407,7 @@ private bool Deploy(string source, IRunContext? runContext, ITestExecutionRecord // Do the deployment. EqtTrace.InfoIf(EqtTrace.IsInfoEnabled, "MSTestExecutor: Using deployment directory {0} for source {1}.", runDirectories.OutDirectory, source); - IEnumerable warnings = Deploy(new List(deploymentItems), source, runDirectories.OutDirectory, GetTestResultsDirectory(runContext)); + IEnumerable warnings = Deploy([.. deploymentItems], source, runDirectories.OutDirectory, GetTestResultsDirectory(runContext)); // Log warnings LogWarnings(testExecutionRecorder, warnings); diff --git a/src/Adapter/MSTestAdapter.PlatformServices/Utilities/ReflectionUtility.cs b/src/Adapter/MSTestAdapter.PlatformServices/Utilities/ReflectionUtility.cs index b9fe555b32..1ef57c713c 100644 --- a/src/Adapter/MSTestAdapter.PlatformServices/Utilities/ReflectionUtility.cs +++ b/src/Adapter/MSTestAdapter.PlatformServices/Utilities/ReflectionUtility.cs @@ -126,7 +126,7 @@ internal static List GetCustomAttributes(Assembly assembly, Type type { if (!assembly.ReflectionOnly) { - return assembly.GetCustomAttributes(type).ToList(); + return [.. assembly.GetCustomAttributes(type)]; } List customAttributes = [.. CustomAttributeData.GetCustomAttributes(assembly)]; @@ -195,8 +195,8 @@ internal static List GetCustomAttributes(Assembly assembly, Type type constructorArguments.Add(list.ToArray(parameterType.GetElementType())); } - ConstructorInfo constructor = attributeType.GetConstructor(constructorParameters.ToArray()); - attribute = constructor.Invoke(constructorArguments.ToArray()); + ConstructorInfo constructor = attributeType.GetConstructor([.. constructorParameters]); + attribute = constructor.Invoke([.. constructorArguments]); foreach (CustomAttributeNamedArgument namedArgument in attributeData.NamedArguments) { diff --git a/src/Adapter/MSTestAdapter.PlatformServices/Utilities/VSInstallationUtilities.cs b/src/Adapter/MSTestAdapter.PlatformServices/Utilities/VSInstallationUtilities.cs index 3083c603c8..07b01ef1dc 100644 --- a/src/Adapter/MSTestAdapter.PlatformServices/Utilities/VSInstallationUtilities.cs +++ b/src/Adapter/MSTestAdapter.PlatformServices/Utilities/VSInstallationUtilities.cs @@ -11,9 +11,9 @@ namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Uti /// Utilities to get Visual Studio installation paths. /// #if NET6_0_OR_GREATER -[Obsolete(Constants.PublicTypeObsoleteMessage, DiagnosticId = "MSTESTOBS")] +[Obsolete(TestTools.UnitTesting.FrameworkConstants.PublicTypeObsoleteMessage, DiagnosticId = "MSTESTOBS")] #else -[Obsolete(Constants.PublicTypeObsoleteMessage)] +[Obsolete(TestTools.UnitTesting.FrameworkConstants.PublicTypeObsoleteMessage)] #endif public static class VSInstallationUtilities { diff --git a/src/Analyzers/MSTest.Analyzers.CodeFixes/AddTestClassFixer.cs b/src/Analyzers/MSTest.Analyzers.CodeFixes/AddTestClassFixer.cs index 2b6488f8de..bca1789d1b 100644 --- a/src/Analyzers/MSTest.Analyzers.CodeFixes/AddTestClassFixer.cs +++ b/src/Analyzers/MSTest.Analyzers.CodeFixes/AddTestClassFixer.cs @@ -52,13 +52,36 @@ public override async Task RegisterCodeFixesAsync(CodeFixContext context) // Find the type declaration identified by the diagnostic. TypeDeclarationSyntax declaration = syntaxToken.Parent.AncestorsAndSelf().OfType().First(); - // Register a code action that will invoke the fix. - context.RegisterCodeFix( - CodeAction.Create( - title: CodeFixResources.AddTestClassFix, - createChangedDocument: c => AddTestClassAttributeAsync(context.Document, declaration, c), - equivalenceKey: $"{nameof(AddTestClassFixer)}_{diagnostic.Id}"), - diagnostic); + // For structs and record structs, we need to change them to classes/record classes since [TestClass] cannot be applied to structs + if (declaration is StructDeclarationSyntax) + { + context.RegisterCodeFix( + CodeAction.Create( + title: CodeFixResources.ChangeStructToClassAndAddTestClassFix, + createChangedDocument: c => ChangeStructToClassAndAddTestClassAttributeAsync(context.Document, declaration, c), + equivalenceKey: $"{nameof(AddTestClassFixer)}_ChangeStructToClass_{diagnostic.Id}"), + diagnostic); + } + else if (declaration is RecordDeclarationSyntax recordDeclaration + && recordDeclaration.ClassOrStructKeyword.IsKind(SyntaxKind.StructKeyword)) + { + context.RegisterCodeFix( + CodeAction.Create( + title: CodeFixResources.ChangeStructToClassAndAddTestClassFix, + createChangedDocument: c => ChangeRecordStructToRecordClassAndAddTestClassAttributeAsync(context.Document, recordDeclaration, c), + equivalenceKey: $"{nameof(AddTestClassFixer)}_ChangeRecordStructToClass_{diagnostic.Id}"), + diagnostic); + } + else + { + // For classes and record classes, just add the [TestClass] attribute + context.RegisterCodeFix( + CodeAction.Create( + title: CodeFixResources.AddTestClassFix, + createChangedDocument: c => AddTestClassAttributeAsync(context.Document, declaration, c), + equivalenceKey: $"{nameof(AddTestClassFixer)}_{diagnostic.Id}"), + diagnostic); + } } private static async Task AddTestClassAttributeAsync(Document document, TypeDeclarationSyntax typeDeclaration, CancellationToken cancellationToken) @@ -75,4 +98,67 @@ private static async Task AddTestClassAttributeAsync(Document document SyntaxNode newRoot = editor.GetChangedRoot(); return document.WithSyntaxRoot(newRoot); } + + private static async Task ChangeStructToClassAndAddTestClassAttributeAsync(Document document, TypeDeclarationSyntax structDeclaration, CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + DocumentEditor editor = await DocumentEditor.CreateAsync(document, cancellationToken).ConfigureAwait(false); + + // Create the [TestClass] attribute + AttributeSyntax testClassAttribute = SyntaxFactory.Attribute(SyntaxFactory.ParseName("TestClass")); + AttributeListSyntax attributeList = SyntaxFactory.AttributeList(SyntaxFactory.SingletonSeparatedList(testClassAttribute)); + + // Convert struct to class + ClassDeclarationSyntax classDeclaration = SyntaxFactory.ClassDeclaration(structDeclaration.Identifier) + .WithModifiers(structDeclaration.Modifiers) + .WithTypeParameterList(structDeclaration.TypeParameterList) + .WithConstraintClauses(structDeclaration.ConstraintClauses) + .WithBaseList(structDeclaration.BaseList) + .WithMembers(structDeclaration.Members) + .WithAttributeLists(structDeclaration.AttributeLists.Add(attributeList)) + .WithLeadingTrivia(structDeclaration.GetLeadingTrivia()) + .WithTrailingTrivia(structDeclaration.GetTrailingTrivia()); + + editor.ReplaceNode(structDeclaration, classDeclaration); + + SyntaxNode newRoot = editor.GetChangedRoot(); + return document.WithSyntaxRoot(newRoot); + } + + private static async Task ChangeRecordStructToRecordClassAndAddTestClassAttributeAsync(Document document, RecordDeclarationSyntax recordStructDeclaration, CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + DocumentEditor editor = await DocumentEditor.CreateAsync(document, cancellationToken).ConfigureAwait(false); + + // Create the [TestClass] attribute + AttributeSyntax testClassAttribute = SyntaxFactory.Attribute(SyntaxFactory.ParseName("TestClass")); + AttributeListSyntax attributeList = SyntaxFactory.AttributeList(SyntaxFactory.SingletonSeparatedList(testClassAttribute)); + + // Filter out readonly modifier since it's not valid for record classes + SyntaxTokenList filteredModifiers = SyntaxFactory.TokenList( + recordStructDeclaration.Modifiers.Where(modifier => !modifier.IsKind(SyntaxKind.ReadOnlyKeyword))); + + // Convert record struct to record class by creating a new RecordDeclarationSyntax + // We need to create a new record declaration instead of just changing the keyword + RecordDeclarationSyntax recordClassDeclaration = SyntaxFactory.RecordDeclaration( + recordStructDeclaration.Keyword, + recordStructDeclaration.Identifier) + .WithModifiers(filteredModifiers) + .WithTypeParameterList(recordStructDeclaration.TypeParameterList) + .WithParameterList(recordStructDeclaration.ParameterList) + .WithBaseList(recordStructDeclaration.BaseList) + .WithConstraintClauses(recordStructDeclaration.ConstraintClauses) + .WithOpenBraceToken(recordStructDeclaration.OpenBraceToken) + .WithMembers(recordStructDeclaration.Members) + .WithCloseBraceToken(recordStructDeclaration.CloseBraceToken) + .WithSemicolonToken(recordStructDeclaration.SemicolonToken) + .WithAttributeLists(recordStructDeclaration.AttributeLists.Add(attributeList)) + .WithLeadingTrivia(recordStructDeclaration.GetLeadingTrivia()) + .WithTrailingTrivia(recordStructDeclaration.GetTrailingTrivia()); + + editor.ReplaceNode(recordStructDeclaration, recordClassDeclaration); + + SyntaxNode newRoot = editor.GetChangedRoot(); + return document.WithSyntaxRoot(newRoot); + } } diff --git a/src/Analyzers/MSTest.Analyzers.CodeFixes/AssertionArgsShouldAvoidConditionalAccessFixer.cs b/src/Analyzers/MSTest.Analyzers.CodeFixes/AssertionArgsShouldAvoidConditionalAccessFixer.cs index 830253f2e4..8e5eefd5e2 100644 --- a/src/Analyzers/MSTest.Analyzers.CodeFixes/AssertionArgsShouldAvoidConditionalAccessFixer.cs +++ b/src/Analyzers/MSTest.Analyzers.CodeFixes/AssertionArgsShouldAvoidConditionalAccessFixer.cs @@ -40,7 +40,7 @@ private sealed class CustomFixAll : DocumentBasedFixAllProvider protected override async Task FixAllAsync(FixAllContext fixAllContext, Document document, ImmutableArray diagnostics) { SyntaxNode root = await document.GetRequiredSyntaxRootAsync(fixAllContext.CancellationToken).ConfigureAwait(false); - DocumentEditor editor = await DocumentEditor.CreateAsync(document, fixAllContext.CancellationToken); + DocumentEditor editor = await DocumentEditor.CreateAsync(document, fixAllContext.CancellationToken).ConfigureAwait(false); Document currentDocument = document; foreach (Diagnostic diagnostic in diagnostics) { diff --git a/src/Analyzers/MSTest.Analyzers.CodeFixes/AvoidAssertFormatParametersFixer.cs b/src/Analyzers/MSTest.Analyzers.CodeFixes/AvoidAssertFormatParametersFixer.cs new file mode 100644 index 0000000000..da9a71e2be --- /dev/null +++ b/src/Analyzers/MSTest.Analyzers.CodeFixes/AvoidAssertFormatParametersFixer.cs @@ -0,0 +1,287 @@ +// 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.Operations; +using Microsoft.CodeAnalysis.Simplification; + +using MSTest.Analyzers.Helpers; + +namespace MSTest.Analyzers.CodeFixes; + +/// +/// Code fix for MSTEST0053: Avoid using Assert methods with format parameters. +/// +[ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(AvoidAssertFormatParametersFixer))] +[Shared] +public sealed class AvoidAssertFormatParametersFixer : CodeFixProvider +{ + /// + public override ImmutableArray FixableDiagnosticIds { get; } + = ImmutableArray.Create(DiagnosticIds.AvoidAssertFormatParametersRuleId); + + /// + public override FixAllProvider GetFixAllProvider() + => WellKnownFixAllProviders.BatchFixer; + + /// + public override async Task RegisterCodeFixesAsync(CodeFixContext context) + { + SyntaxNode root = await context.Document.GetRequiredSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); + Diagnostic diagnostic = context.Diagnostics[0]; + if (root.FindNode(context.Span, getInnermostNodeForTie: true) is InvocationExpressionSyntax invocation) + { + RegisterStringFormatCodeFix(context, diagnostic, root, invocation); + await RegisterInterpolatedStringCodeFixAsync(context, diagnostic, root, invocation).ConfigureAwait(false); + } + } + + private static void RegisterStringFormatCodeFix(CodeFixContext context, Diagnostic diagnostic, SyntaxNode root, InvocationExpressionSyntax invocation) + { + var codeAction = CodeAction.Create( + title: CodeFixResources.AvoidAssertFormatParametersUseStringFormat, + createChangedDocument: ct => CreateStringFormatFixAsync(context.Document, root, invocation, ct), + equivalenceKey: $"{nameof(AvoidAssertFormatParametersFixer)}_StringFormat"); + + context.RegisterCodeFix(codeAction, diagnostic); + } + + private static async Task RegisterInterpolatedStringCodeFixAsync(CodeFixContext context, Diagnostic diagnostic, SyntaxNode root, InvocationExpressionSyntax invocation) + { + // Only offer interpolated string fix for simple cases + if (await CanConvertToInterpolatedStringAsync(context.Document, invocation, context.CancellationToken).ConfigureAwait(false)) + { + var codeAction = CodeAction.Create( + title: CodeFixResources.AvoidAssertFormatParametersUseInterpolatedString, + createChangedDocument: ct => CreateInterpolatedStringFixAsync(context.Document, root, invocation, ct), + equivalenceKey: $"{nameof(AvoidAssertFormatParametersFixer)}_InterpolatedString"); + + context.RegisterCodeFix(codeAction, diagnostic); + } + } + + private static async Task CreateStringFormatFixAsync(Document document, SyntaxNode root, InvocationExpressionSyntax invocation, CancellationToken cancellationToken) + { + ArgumentListSyntax oldArgumentList = invocation.ArgumentList; + if (oldArgumentList.Arguments.Count < 2) + { + return document; + } + + SemanticModel semanticModel = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); + if (semanticModel.GetOperation(invocation, cancellationToken) is not IInvocationOperation invocationOperation) + { + return document; + } + + if (!TryGetMessageAndMessageArgsArguments(invocationOperation, out IArgumentOperation? messageArgumentOperation, out IArgumentOperation? paramsArgumentOperation)) + { + return document; + } + + if (paramsArgumentOperation is null || messageArgumentOperation is null) + { + return document; + } + + ArgumentListSyntax newArgumentList = oldArgumentList; + var formatArgument = (ArgumentSyntax)messageArgumentOperation.Syntax; + if (paramsArgumentOperation.ArgumentKind == ArgumentKind.ParamArray) + { + ImmutableArray elementValues = ((IArrayCreationOperation)paramsArgumentOperation.Value).Initializer!.ElementValues; + IEnumerable paramsArguments = elementValues.Select(e => (ArgumentSyntax)e.Syntax.Parent!); + + InvocationExpressionSyntax stringFormatInvocation = CreateStringFormatCall([formatArgument, .. paramsArguments]); + + newArgumentList = newArgumentList.ReplaceNode(formatArgument, formatArgument.WithExpression(stringFormatInvocation)); + foreach (IOperation element in elementValues.OrderByDescending(e => oldArgumentList.Arguments.IndexOf((ArgumentSyntax)e.Syntax.Parent!))) + { + newArgumentList = newArgumentList.WithArguments(newArgumentList.Arguments.RemoveAt(oldArgumentList.Arguments.IndexOf((ArgumentSyntax)element.Syntax.Parent!))); + } + } + else if (paramsArgumentOperation.ArgumentKind == ArgumentKind.Explicit) + { + var paramsArgumentSyntax = (ArgumentSyntax)paramsArgumentOperation.Syntax; + InvocationExpressionSyntax stringFormatInvocation = CreateStringFormatCall([formatArgument, paramsArgumentSyntax]); + + newArgumentList = newArgumentList.ReplaceNode(formatArgument, formatArgument.WithExpression(stringFormatInvocation)); + newArgumentList = newArgumentList.WithArguments(newArgumentList.Arguments.RemoveAt(oldArgumentList.Arguments.IndexOf(paramsArgumentSyntax))); + } + + return document.WithSyntaxRoot(root.ReplaceNode(oldArgumentList, newArgumentList)); + + static InvocationExpressionSyntax CreateStringFormatCall(IEnumerable arguments) + => SyntaxFactory.InvocationExpression( + SyntaxFactory.MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + SyntaxFactory.PredefinedType(SyntaxFactory.Token(SyntaxKind.StringKeyword)), + SyntaxFactory.IdentifierName("Format")), + SyntaxFactory.ArgumentList(SyntaxFactory.SeparatedList(arguments))); + } + + private static async Task CreateInterpolatedStringFixAsync( + Document document, + SyntaxNode root, + InvocationExpressionSyntax invocation, + CancellationToken cancellationToken) + { + ArgumentListSyntax oldArgumentList = invocation.ArgumentList; + if (oldArgumentList.Arguments.Count < 2) + { + return document; + } + + SemanticModel semanticModel = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); + if (semanticModel.GetOperation(invocation, cancellationToken) is not IInvocationOperation invocationOperation) + { + return document; + } + + if (!TryGetMessageAndMessageArgsArguments(invocationOperation, out IArgumentOperation? messageArgumentOperation, out IArgumentOperation? paramsArgumentOperation)) + { + return document; + } + + ImmutableArray elementValues = ((IArrayCreationOperation)paramsArgumentOperation.Value).Initializer!.ElementValues; + ArgumentSyntax[] paramsArguments = [.. elementValues.Select(e => (ArgumentSyntax)e.Syntax.Parent!)]; + ArgumentListSyntax newArgumentList = oldArgumentList; + + var formatArgument = (ArgumentSyntax)messageArgumentOperation.Syntax; + + if (TryCreateInterpolatedString(formatArgument, paramsArguments, out InterpolatedStringExpressionSyntax? interpolatedString)) + { + newArgumentList = newArgumentList.ReplaceNode(formatArgument, formatArgument.WithExpression(interpolatedString)); + } + + foreach (IOperation element in elementValues.OrderByDescending(e => oldArgumentList.Arguments.IndexOf((ArgumentSyntax)e.Syntax.Parent!))) + { + newArgumentList = newArgumentList.WithArguments(newArgumentList.Arguments.RemoveAt(oldArgumentList.Arguments.IndexOf((ArgumentSyntax)element.Syntax.Parent!))); + } + + return document.WithSyntaxRoot(root.ReplaceNode(oldArgumentList, newArgumentList)); + } + + private static async Task CanConvertToInterpolatedStringAsync( + Document document, + InvocationExpressionSyntax invocation, + CancellationToken cancellationToken) + { + SeparatedSyntaxList arguments = invocation.ArgumentList.Arguments; + if (arguments.Count < 2) + { + return false; + } + + SemanticModel semanticModel = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); + if (semanticModel.GetOperation(invocation, cancellationToken) is not IInvocationOperation invocationOperation || + !TryGetMessageAndMessageArgsArguments(invocationOperation, out IArgumentOperation? messageArgumentOperation, out IArgumentOperation? paramsArgumentOperation) || + paramsArgumentOperation.ArgumentKind != ArgumentKind.ParamArray || + messageArgumentOperation.Syntax is not ArgumentSyntax formatArgument) + { + return false; + } + + // We can only offer a fix if the message is a string literal already. + return formatArgument.Expression is LiteralExpressionSyntax literal && + literal.Token.IsKind(SyntaxKind.StringLiteralToken); + } + + private static bool TryCreateInterpolatedString( + ArgumentSyntax formatArgument, + ArgumentSyntax[] paramsArguments, + [NotNullWhen(true)] out InterpolatedStringExpressionSyntax? interpolatedString) + { + interpolatedString = null; + if (formatArgument.Expression is not LiteralExpressionSyntax literal || + !literal.Token.IsKind(SyntaxKind.StringLiteralToken)) + { + return false; + } + + string formatString = literal.Token.ValueText; + var interpolatedContents = new List(); + int currentIndex = 0; + + for (int i = 0; i < formatString.Length; i++) + { + if (formatString[i] == '{' && i + 1 < formatString.Length) + { + // Add text before the placeholder + if (i > currentIndex) + { + string text = formatString[currentIndex..i]; + interpolatedContents.Add(SyntaxFactory.InterpolatedStringText( + SyntaxFactory.Token(SyntaxTriviaList.Empty, SyntaxKind.InterpolatedStringTextToken, text, text, SyntaxTriviaList.Empty))); + } + + // Find the end of the placeholder + int closeIndex = formatString.IndexOf('}', i + 1); + if (closeIndex == -1) + { + return false; // Invalid format string + } + + // Extract the placeholder index + string placeholder = formatString.Substring(i + 1, closeIndex - i - 1); + if (int.TryParse(placeholder, NumberStyles.Integer, CultureInfo.InvariantCulture, out int paramIndex) && paramIndex < paramsArguments.Length) + { + // Create interpolation expression + InterpolationSyntax interpolation = SyntaxFactory.Interpolation( + SyntaxFactory.ParenthesizedExpression( + paramsArguments[paramIndex].Expression).WithAdditionalAnnotations(Simplifier.Annotation)); + interpolatedContents.Add(interpolation); + } + else + { + return false; // Invalid or out-of-range parameter index + } + + currentIndex = closeIndex + 1; + i = closeIndex; + } + } + + // Add remaining text + if (currentIndex < formatString.Length) + { + string text = formatString[currentIndex..]; + interpolatedContents.Add(SyntaxFactory.InterpolatedStringText( + SyntaxFactory.Token(SyntaxTriviaList.Empty, SyntaxKind.InterpolatedStringTextToken, text, text, SyntaxTriviaList.Empty))); + } + + interpolatedString = SyntaxFactory.InterpolatedStringExpression( + SyntaxFactory.Token(SyntaxKind.InterpolatedStringStartToken), + SyntaxFactory.List(interpolatedContents), + SyntaxFactory.Token(SyntaxKind.InterpolatedStringEndToken)); + + return true; + } + + private static bool TryGetMessageAndMessageArgsArguments( + IInvocationOperation invocationOperation, + [NotNullWhen(true)] out IArgumentOperation? messageArgument, + [NotNullWhen(true)] out IArgumentOperation? messageArgsArgument) + { + ImmutableArray parameters = invocationOperation.TargetMethod.Parameters; + if (parameters.Length < 2 || + parameters.SingleOrDefault(p => p.Name == "message" && p.Type.SpecialType == SpecialType.System_String) is not IParameterSymbol messageParameter) + { + messageArgument = null; + messageArgsArgument = null; + return false; + } + + messageArgument = invocationOperation.Arguments.SingleOrDefault(arg => arg.Parameter?.Ordinal == messageParameter.Ordinal); + messageArgsArgument = invocationOperation.Arguments.SingleOrDefault(arg => arg.Parameter?.Ordinal == invocationOperation.TargetMethod.Parameters.Length - 1); + return messageArgument is not null && messageArgsArgument is not null; + } +} diff --git a/src/Analyzers/MSTest.Analyzers.CodeFixes/AvoidExplicitDynamicDataSourceTypeFixer.cs b/src/Analyzers/MSTest.Analyzers.CodeFixes/AvoidExplicitDynamicDataSourceTypeFixer.cs new file mode 100644 index 0000000000..9811d55ce5 --- /dev/null +++ b/src/Analyzers/MSTest.Analyzers.CodeFixes/AvoidExplicitDynamicDataSourceTypeFixer.cs @@ -0,0 +1,83 @@ +// 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 Analyzer.Utilities.Extensions; + +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CodeActions; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Editing; +using Microsoft.CodeAnalysis.Text; + +using MSTest.Analyzers.Helpers; + +namespace MSTest.Analyzers; + +/// +/// Code fixer for . +/// +[ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(AvoidExplicitDynamicDataSourceTypeFixer))] +[Shared] +public sealed class AvoidExplicitDynamicDataSourceTypeFixer : CodeFixProvider +{ + /// + public sealed override ImmutableArray FixableDiagnosticIds { get; } + = ImmutableArray.Create(DiagnosticIds.AvoidExplicitDynamicDataSourceTypeRuleId); + + /// + 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); + Diagnostic diagnostic = context.Diagnostics[0]; + TextSpan diagnosticSpan = diagnostic.Location.SourceSpan; + + if (root.FindNode(diagnosticSpan) is not AttributeSyntax attributeSyntax || + attributeSyntax.ArgumentList is not AttributeArgumentListSyntax argumentList) + { + return; + } + + context.RegisterCodeFix( + CodeAction.Create( + CodeFixResources.RemoveDynamicDataSourceTypeFix, + ct => RemoveDynamicDataSourceTypeAsync(context.Document, argumentList, ct), + nameof(AvoidExplicitDynamicDataSourceTypeFixer)), + context.Diagnostics); + } + + private static async Task RemoveDynamicDataSourceTypeAsync(Document document, AttributeArgumentListSyntax argumentList, CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + + DocumentEditor editor = await DocumentEditor.CreateAsync(document, cancellationToken).ConfigureAwait(false); + SemanticModel semanticModel = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); + + if (!semanticModel.Compilation.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftVisualStudioTestToolsUnitTestingDynamicDataSourceType, out INamedTypeSymbol? dynamicDataSourceTypeSymbol)) + { + return document; + } + + foreach (AttributeArgumentSyntax argument in argumentList.Arguments) + { + Microsoft.CodeAnalysis.TypeInfo typeInfo = semanticModel.GetTypeInfo(argument.Expression, cancellationToken); + if (SymbolEqualityComparer.Default.Equals(typeInfo.Type, dynamicDataSourceTypeSymbol)) + { + editor.ReplaceNode(argumentList, argumentList.WithArguments(argumentList.Arguments.Remove(argument))); + break; + } + } + + return editor.GetChangedDocument(); + } +} diff --git a/src/Analyzers/MSTest.Analyzers.CodeFixes/CodeFixResources.Designer.cs b/src/Analyzers/MSTest.Analyzers.CodeFixes/CodeFixResources.Designer.cs deleted file mode 100644 index 9cab8f2e12..0000000000 --- a/src/Analyzers/MSTest.Analyzers.CodeFixes/CodeFixResources.Designer.cs +++ /dev/null @@ -1,243 +0,0 @@ -//------------------------------------------------------------------------------ -// -// 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; - - - /// - /// 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 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; - } - } - - /// - /// 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 Add '[TestClass]'. - /// - internal static string AddTestClassFix { - get { - return ResourceManager.GetString("AddTestClassFix", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Add '[TestMethod]'. - /// - internal static string AddTestMethodAttributeFix { - get { - return ResourceManager.GetString("AddTestMethodAttributeFix", resourceCulture); - } - } - - /// - /// 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 Move conditional access in assertion to separate 'Assert.IsNotNull' check. - /// - internal static string AssertionArgsShouldAvoidConditionalAccessFix { - get { - return ResourceManager.GetString("AssertionArgsShouldAvoidConditionalAccessFix", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Use '{0}'. - /// - internal static string AvoidAssertAreSameWithValueTypesFix { - get { - return ResourceManager.GetString("AvoidAssertAreSameWithValueTypesFix", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Change method accessibility to 'private'. - /// - internal static string ChangeMethodAccessibilityToPrivateFix { - get { - return ResourceManager.GetString("ChangeMethodAccessibilityToPrivateFix", 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); - } - } - - /// - /// Looks up a localized string similar to Replace TestInitialize method with constructor. - /// - internal static string ReplaceWithConstructorFix { - get { - return ResourceManager.GetString("ReplaceWithConstructorFix", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Replace TestCleanup with Dispose method. - /// - internal static string ReplaceWithDisposeFix { - get { - return ResourceManager.GetString("ReplaceWithDisposeFix", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Replace the assertion with 'Assert.Fail()'. - /// - internal static string ReplaceWithFailAssertionFix { - get { - return ResourceManager.GetString("ReplaceWithFailAssertionFix", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Replace 'Dispose' with a TestCleanup method. - /// - internal static string ReplaceWithTestCleanuFix { - get { - return ResourceManager.GetString("ReplaceWithTestCleanuFix", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Replace constructor with TestInitialize method. - /// - internal static string ReplaceWithTestInitializeFix { - get { - return ResourceManager.GetString("ReplaceWithTestInitializeFix", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Fix test class signature. - /// - internal static string TestClassShouldBeValidFix { - get { - return ResourceManager.GetString("TestClassShouldBeValidFix", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Fix test context. - /// - internal static string TestContextShouldBeValidFix { - get { - return ResourceManager.GetString("TestContextShouldBeValidFix", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Fix test method signature. - /// - internal static string TestMethodShouldBeValidFix { - get { - return ResourceManager.GetString("TestMethodShouldBeValidFix", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Use 'Assert.ThrowsException<T>' instead of '[ExpectedException]' attribute. - /// - internal static string UseAssertThrowsExceptionOnLastStatementFix { - get { - return ResourceManager.GetString("UseAssertThrowsExceptionOnLastStatementFix", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Add '[TestMethod]'. - /// - internal static string UseAttributeOnTestMethodFix { - get { - return ResourceManager.GetString("UseAttributeOnTestMethodFix", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Use '{0}'. - /// - internal static string UseNewerAssertThrows { - get { - return ResourceManager.GetString("UseNewerAssertThrows", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Use '{0}'. - /// - internal static string UseProperAssertMethodsFix { - get { - return ResourceManager.GetString("UseProperAssertMethodsFix", resourceCulture); - } - } - } -} diff --git a/src/Analyzers/MSTest.Analyzers.CodeFixes/CodeFixResources.resx b/src/Analyzers/MSTest.Analyzers.CodeFixes/CodeFixResources.resx index 27cd7731e9..b88c2f5b10 100644 --- a/src/Analyzers/MSTest.Analyzers.CodeFixes/CodeFixResources.resx +++ b/src/Analyzers/MSTest.Analyzers.CodeFixes/CodeFixResources.resx @@ -129,6 +129,9 @@ Add '[TestClass]' + + Change to 'class' and add '[TestClass]' + Fix test method signature @@ -177,4 +180,28 @@ Use '{0}' + + Replace 'DataTestMethod' with 'TestMethod' + + + Use 'CooperativeCancellation = true' + + + Use 'Assert.{0}' instead of 'StringAssert' + + + Pass 'TestContext.CancellationToken' argument to method call + + + Remove 'DynamicDataSourceType' parameter to use default 'AutoDetect' + + + Use 'TestContext.CancellationToken' instead + + + Use 'string.Format' instead of format parameters + + + Use interpolated string instead of format parameters + \ No newline at end of file diff --git a/src/Analyzers/MSTest.Analyzers.CodeFixes/FlowTestContextCancellationTokenFixer.cs b/src/Analyzers/MSTest.Analyzers.CodeFixes/FlowTestContextCancellationTokenFixer.cs new file mode 100644 index 0000000000..9a8716502c --- /dev/null +++ b/src/Analyzers/MSTest.Analyzers.CodeFixes/FlowTestContextCancellationTokenFixer.cs @@ -0,0 +1,265 @@ +// 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.Editing; +using Microsoft.CodeAnalysis.Text; + +using MSTest.Analyzers.Helpers; + +using Polyfills; + +namespace MSTest.Analyzers; + +/// +/// Code fixer for . +/// +[ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(FlowTestContextCancellationTokenFixer))] +[Shared] +public sealed class FlowTestContextCancellationTokenFixer : CodeFixProvider +{ + /// + public sealed override ImmutableArray FixableDiagnosticIds { get; } + = ImmutableArray.Create(DiagnosticIds.FlowTestContextCancellationTokenRuleId); + + /// + public override FixAllProvider GetFixAllProvider() + // Use custom FixAllProvider to handle adding TestContext property when needed + => FlowTestContextCancellationTokenFixAllProvider.Instance; + + /// + public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) + { + SyntaxNode root = await context.Document.GetRequiredSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); + Diagnostic diagnostic = context.Diagnostics[0]; + TextSpan diagnosticSpan = diagnostic.Location.SourceSpan; + + // Find the invocation expression identified by the diagnostic + SyntaxNode node = root.FindNode(diagnosticSpan, getInnermostNodeForTie: true); + if (node is not InvocationExpressionSyntax invocationExpression) + { + return; + } + + diagnostic.Properties.TryGetValue(FlowTestContextCancellationTokenAnalyzer.TestContextMemberNamePropertyKey, out string? testContextMemberName); + diagnostic.Properties.TryGetValue(FlowTestContextCancellationTokenAnalyzer.CancellationTokenParameterNamePropertyKey, out string? cancellationTokenParameterName); + diagnostic.Properties.TryGetValue(nameof(FlowTestContextCancellationTokenAnalyzer.TestContextState), out string? testContextState); + + // Register a code action that will invoke the fix + context.RegisterCodeFix( + CodeAction.Create( + title: CodeFixResources.PassCancellationTokenFix, + createChangedDocument: async c => + { + DocumentEditor editor = await DocumentEditor.CreateAsync(context.Document, context.CancellationToken).ConfigureAwait(false); + return ApplyFix(editor, invocationExpression, testContextMemberName, testContextState, cancellationTokenParameterName, adjustedSymbols: null, c); + }, + equivalenceKey: nameof(FlowTestContextCancellationTokenFixer)), + diagnostic); + } + + internal static Document ApplyFix( + DocumentEditor editor, + InvocationExpressionSyntax invocationExpression, + string? testContextMemberName, + string? testContextState, + string? cancellationTokenParameterName, + HashSet? adjustedSymbols, + CancellationToken cancellationToken) + { + if (testContextState == nameof(FlowTestContextCancellationTokenAnalyzer.TestContextState.CouldBeInScopeAsProperty)) + { + Debug.Assert(testContextMemberName is null, "TestContext member name should be null when state is CouldBeInScopeAsProperty"); + AddCancellationTokenArgument(editor, invocationExpression, "TestContext", cancellationTokenParameterName); + TypeDeclarationSyntax? containingTypeDeclaration = invocationExpression.FirstAncestorOrSelf(); + if (containingTypeDeclaration is not null) + { + // adjustedSymbols is null meaning we are only applying a single fix (in that case we add the property). + // If we are in fix all, we then verify if a previous fix has already added the property. + // We only add the property if it wasn't added by a previous fix. + // NOTE: We don't expect GetDeclaredSymbol to return null, but if it did (e.g, error scenario), we add the property. + if (adjustedSymbols is null || + editor.SemanticModel.GetDeclaredSymbol(containingTypeDeclaration, cancellationToken) is not { } symbol || + adjustedSymbols.Add(symbol)) + { + editor.ReplaceNode(containingTypeDeclaration, (containingTypeDeclaration, _) => AddTestContextProperty((TypeDeclarationSyntax)containingTypeDeclaration)); + } + } + } + else if (testContextState == nameof(FlowTestContextCancellationTokenAnalyzer.TestContextState.CouldBeInScopeAsParameter)) + { + Debug.Assert(testContextMemberName is null, "TestContext member name should be null when state is CouldBeInScopeAsParameter"); + AddCancellationTokenArgument(editor, invocationExpression, "testContext", cancellationTokenParameterName); + MethodDeclarationSyntax? containingMethodDeclaration = invocationExpression.FirstAncestorOrSelf(); + + if (containingMethodDeclaration is not null) + { + // adjustedSymbols is null meaning we are only applying a single fix (in that case we add the parameter). + // If we are in fix all, we then verify if a previous fix has already added the parameter. + // We only add the parameter if it wasn't added by a previous fix. + // NOTE: We don't expect GetDeclaredSymbol to return null, but if it did (e.g, error scenario), we add the property. + if (adjustedSymbols is null || + editor.SemanticModel.GetDeclaredSymbol(containingMethodDeclaration, cancellationToken) is not { } symbol || + adjustedSymbols.Add(symbol)) + { + editor.ReplaceNode(containingMethodDeclaration, (containingMethodDeclaration, _) => AddTestContextParameterToMethod((MethodDeclarationSyntax)containingMethodDeclaration)); + } + } + } + else + { + Guard.NotNull(testContextMemberName); + AddCancellationTokenArgument(editor, invocationExpression, testContextMemberName, cancellationTokenParameterName); + } + + return editor.GetChangedDocument(); + } + + internal static void AddCancellationTokenArgument( + DocumentEditor editor, + InvocationExpressionSyntax invocationExpression, + string testContextMemberName, + string? cancellationTokenParameterName) + { + // Find the containing method to determine the context + MethodDeclarationSyntax? containingMethod = invocationExpression.FirstAncestorOrSelf(); + + // Create the TestContext.CancellationToken expression + MemberAccessExpressionSyntax testContextExpression = SyntaxFactory.MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + SyntaxFactory.IdentifierName(testContextMemberName), + SyntaxFactory.IdentifierName("CancellationToken")); + + editor.ReplaceNode(invocationExpression, (node, _) => + { + var invocationExpression = (InvocationExpressionSyntax)node; + ArgumentListSyntax currentArguments = invocationExpression.ArgumentList; + NameColonSyntax? nameColon = cancellationTokenParameterName is null ? null : SyntaxFactory.NameColon(cancellationTokenParameterName); + SeparatedSyntaxList newArguments = currentArguments.Arguments.Add(SyntaxFactory.Argument(nameColon, default, testContextExpression)); + return invocationExpression.WithArgumentList(currentArguments.WithArguments(newArguments)); + }); + } + + internal static MethodDeclarationSyntax AddTestContextParameterToMethod(MethodDeclarationSyntax method) + { + // Create TestContext parameter + ParameterSyntax testContextParameter = SyntaxFactory.Parameter(SyntaxFactory.Identifier("testContext")) + .WithType(SyntaxFactory.IdentifierName("TestContext")); + + // Add the parameter to the method + SeparatedSyntaxList updatedParameterList = method.ParameterList.Parameters.Count == 0 + ? SyntaxFactory.SingletonSeparatedList(testContextParameter) + : method.ParameterList.Parameters.Add(testContextParameter); + + return method.WithParameterList(method.ParameterList.WithParameters(updatedParameterList)); + } + + internal static TypeDeclarationSyntax AddTestContextProperty(TypeDeclarationSyntax typeDeclaration) + { + PropertyDeclarationSyntax testContextProperty = SyntaxFactory.PropertyDeclaration( + SyntaxFactory.IdentifierName("TestContext"), + "TestContext") + .WithModifiers(SyntaxFactory.TokenList(SyntaxFactory.Token(SyntaxKind.PublicKeyword))) + .WithAccessorList(SyntaxFactory.AccessorList( + SyntaxFactory.List(new[] + { + SyntaxFactory.AccessorDeclaration(SyntaxKind.GetAccessorDeclaration) + .WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.SemicolonToken)), + SyntaxFactory.AccessorDeclaration(SyntaxKind.SetAccessorDeclaration) + .WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.SemicolonToken)), + }))); + + return typeDeclaration.AddMembers(testContextProperty); + } +} + +/// +/// Custom FixAllProvider for that can add TestContext property when needed. +/// This ensures that when multiple fixes are applied to the same class, the TestContext property is added only once. +/// +internal sealed class FlowTestContextCancellationTokenFixAllProvider : FixAllProvider +{ + public static readonly FlowTestContextCancellationTokenFixAllProvider Instance = new(); + + private FlowTestContextCancellationTokenFixAllProvider() + { + } + + public override Task GetFixAsync(FixAllContext fixAllContext) + => Task.FromResult(new FixAllCodeAction(fixAllContext)); + + private sealed class FixAllCodeAction : CodeAction + { + private readonly FixAllContext _fixAllContext; + + public FixAllCodeAction(FixAllContext fixAllContext) + => _fixAllContext = fixAllContext; + + public override string Title => CodeFixResources.PassCancellationTokenFix; + + public override string? EquivalenceKey => nameof(FlowTestContextCancellationTokenFixer); + + protected override async Task GetChangedSolutionAsync(CancellationToken cancellationToken) + { + FixAllContext fixAllContext = _fixAllContext; + var editor = new SolutionEditor(fixAllContext.Solution); + var fixedSymbols = new HashSet(SymbolEqualityComparer.Default); + + if (fixAllContext.Scope == FixAllScope.Document) + { + DocumentEditor documentEditor = await editor.GetDocumentEditorAsync(fixAllContext.Document!.Id, cancellationToken).ConfigureAwait(false); + foreach (Diagnostic diagnostic in await fixAllContext.GetDocumentDiagnosticsAsync(fixAllContext.Document!).ConfigureAwait(false)) + { + FixOneDiagnostic(documentEditor, diagnostic, fixedSymbols, cancellationToken); + } + } + else if (fixAllContext.Scope == FixAllScope.Project) + { + await FixAllInProjectAsync(fixAllContext, fixAllContext.Project, editor, fixedSymbols, cancellationToken).ConfigureAwait(false); + } + else if (fixAllContext.Scope == FixAllScope.Solution) + { + foreach (Project project in fixAllContext.Solution.Projects) + { + await FixAllInProjectAsync(fixAllContext, project, editor, fixedSymbols, cancellationToken).ConfigureAwait(false); + } + } + + return editor.GetChangedSolution(); + } + + private static async Task FixAllInProjectAsync(FixAllContext fixAllContext, Project project, SolutionEditor editor, HashSet fixedSymbols, CancellationToken cancellationToken) + { + foreach (Diagnostic diagnostic in await fixAllContext.GetAllDiagnosticsAsync(project).ConfigureAwait(false)) + { + DocumentId documentId = editor.OriginalSolution.GetDocumentId(diagnostic.Location.SourceTree)!; + DocumentEditor documentEditor = await editor.GetDocumentEditorAsync(documentId, cancellationToken).ConfigureAwait(false); + FixOneDiagnostic(documentEditor, diagnostic, fixedSymbols, cancellationToken); + } + } + + private static void FixOneDiagnostic(DocumentEditor documentEditor, Diagnostic diagnostic, HashSet fixedSymbols, CancellationToken cancellationToken) + { + SyntaxNode node = documentEditor.OriginalRoot.FindNode(diagnostic.Location.SourceSpan, getInnermostNodeForTie: true); + if (node is not InvocationExpressionSyntax invocationExpression) + { + return; + } + + diagnostic.Properties.TryGetValue(FlowTestContextCancellationTokenAnalyzer.TestContextMemberNamePropertyKey, out string? testContextMemberName); + diagnostic.Properties.TryGetValue(FlowTestContextCancellationTokenAnalyzer.CancellationTokenParameterNamePropertyKey, out string? cancellationTokenParameterName); + diagnostic.Properties.TryGetValue(nameof(FlowTestContextCancellationTokenAnalyzer.TestContextState), out string? testContextState); + + FlowTestContextCancellationTokenFixer.ApplyFix(documentEditor, invocationExpression, testContextMemberName, testContextState, cancellationTokenParameterName, fixedSymbols, cancellationToken); + } + } +} diff --git a/src/Analyzers/MSTest.Analyzers.CodeFixes/MSTest.Analyzers.CodeFixes.csproj b/src/Analyzers/MSTest.Analyzers.CodeFixes/MSTest.Analyzers.CodeFixes.csproj index 90c2706c8f..525d079c19 100644 --- a/src/Analyzers/MSTest.Analyzers.CodeFixes/MSTest.Analyzers.CodeFixes.csproj +++ b/src/Analyzers/MSTest.Analyzers.CodeFixes/MSTest.Analyzers.CodeFixes.csproj @@ -14,9 +14,4 @@ - - - - - diff --git a/src/Analyzers/MSTest.Analyzers.CodeFixes/PreferConstructorOverTestInitializeFixer.cs b/src/Analyzers/MSTest.Analyzers.CodeFixes/PreferConstructorOverTestInitializeFixer.cs index efeecd6dbd..2c0f2df3fb 100644 --- a/src/Analyzers/MSTest.Analyzers.CodeFixes/PreferConstructorOverTestInitializeFixer.cs +++ b/src/Analyzers/MSTest.Analyzers.CodeFixes/PreferConstructorOverTestInitializeFixer.cs @@ -68,7 +68,7 @@ private static async Task ReplaceTestInitializeWithConstructorAsync(Do { ConstructorDeclarationSyntax? existingConstructor = containingClass.Members .OfType() - .FirstOrDefault(); + .FirstOrDefault(c => !c.Modifiers.Any(SyntaxKind.StaticKeyword)); // Move the body of the TestInitialize method BlockSyntax? testInitializeBody = testInitializeMethod.Body; @@ -81,7 +81,7 @@ private static async Task ReplaceTestInitializeWithConstructorAsync(Do // If a constructor already exists, append the body of the TestInitialize method to it if (existingConstructor.Body != null) { - BlockSyntax newConstructorBody = existingConstructor.Body.AddStatements(testInitializeStatements ?? Array.Empty()); + BlockSyntax newConstructorBody = existingConstructor.Body.AddStatements(testInitializeStatements ?? []); newConstructor = existingConstructor.WithBody(newConstructorBody); } else @@ -90,19 +90,20 @@ private static async Task ReplaceTestInitializeWithConstructorAsync(Do } editor.ReplaceNode(existingConstructor, newConstructor); + + // Remove the TestInitialize method + editor.RemoveNode(testInitializeMethod); } else { // Create a new constructor with the TestInitialize body if one doesn't exist - ConstructorDeclarationSyntax constructor = SyntaxFactory.ConstructorDeclaration(containingClass.Identifier) + ConstructorDeclarationSyntax constructor = SyntaxFactory.ConstructorDeclaration(containingClass.Identifier.WithoutTrivia()) .WithModifiers(SyntaxFactory.TokenList(SyntaxFactory.Token(SyntaxKind.PublicKeyword))) .WithBody(testInitializeBody); - editor.AddMember(containingClass, constructor); + // Insert the constructor at the position of the existing TestInitialize method + editor.ReplaceNode(testInitializeMethod, constructor); } - - // Remove the TestInitialize method - editor.RemoveNode(testInitializeMethod); } return editor.GetChangedDocument(); diff --git a/src/Analyzers/MSTest.Analyzers.CodeFixes/PreferDisposeOverTestCleanupFixer.cs b/src/Analyzers/MSTest.Analyzers.CodeFixes/PreferDisposeOverTestCleanupFixer.cs index 9911ee7287..309ccadb67 100644 --- a/src/Analyzers/MSTest.Analyzers.CodeFixes/PreferDisposeOverTestCleanupFixer.cs +++ b/src/Analyzers/MSTest.Analyzers.CodeFixes/PreferDisposeOverTestCleanupFixer.cs @@ -69,7 +69,7 @@ private static bool IsTestCleanupMethodValid(MethodDeclarationSyntax methodDecla methodDeclaration.ReturnType is PredefinedTypeSyntax predefinedType && predefinedType.Keyword.IsKind(SyntaxKind.VoidKeyword); - private static async Task AddDisposeAndBaseClassAsync( + private static async Task AddDisposeAndBaseClassAsync( Document document, MethodDeclarationSyntax testCleanupMethod, TypeDeclarationSyntax containingType, @@ -82,23 +82,54 @@ private static async Task AddDisposeAndBaseClassAsync( SyntaxGenerator generator = editor.Generator; TypeDeclarationSyntax newParent = containingType; INamedTypeSymbol? iDisposableSymbol = semanticModel.Compilation.GetTypeByMetadataName(WellKnownTypeNames.SystemIDisposable); - INamedTypeSymbol? testCleanupAttributeSymbol = semanticModel.Compilation.GetTypeByMetadataName(WellKnownTypeNames.MicrosoftVisualStudioTestToolsUnitTestingTestCleanupAttribute); // Move the code from TestCleanup to Dispose method - MethodDeclarationSyntax? existingDisposeMethod = containingType.Members - .OfType() - .FirstOrDefault(m => semanticModel.GetDeclaredSymbol(m) is IMethodSymbol methodSymbol && methodSymbol.IsDisposeImplementation(iDisposableSymbol)); + // For partial classes, we need to check all parts of the type, not just the current partial declaration + Document? disposeDocument = null; + MethodDeclarationSyntax? existingDisposeMethod = null; + if (semanticModel.GetDeclaredSymbol(containingType, cancellationToken) is INamedTypeSymbol typeSymbol) + { + // Find existing Dispose method in any part of the partial class + IMethodSymbol? existingDisposeSymbol = typeSymbol.GetMembers("Dispose") + .OfType() + .FirstOrDefault(m => m.IsDisposeImplementation(iDisposableSymbol)); + + if (existingDisposeSymbol is not null) + { + // Find the syntax node for the existing Dispose method + foreach (SyntaxReference syntaxRef in existingDisposeSymbol.DeclaringSyntaxReferences) + { + if (await syntaxRef.GetSyntaxAsync(cancellationToken).ConfigureAwait(false) is MethodDeclarationSyntax methodSyntax) + { + existingDisposeMethod = methodSyntax; + // Find the document containing the Dispose method + foreach (Document projectDocument in document.Project.Documents) + { + SyntaxTree? docSyntaxTree = await projectDocument.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false); + if (docSyntaxTree == syntaxRef.SyntaxTree) + { + disposeDocument = projectDocument; + break; + } + } + + break; + } + } + } + } BlockSyntax? cleanupBody = testCleanupMethod.Body; + Solution solution = document.Project.Solution; - if (existingDisposeMethod != null) + if (existingDisposeMethod is not null && disposeDocument is not null) { // Append the TestCleanup body to the existing Dispose method StatementSyntax[]? cleanupStatements = cleanupBody?.Statements.ToArray(); MethodDeclarationSyntax newDisposeMethod; - if (existingDisposeMethod.Body != null) + if (existingDisposeMethod.Body is not null) { - BlockSyntax newDisposeBody = existingDisposeMethod.Body.AddStatements(cleanupStatements ?? Array.Empty()); + BlockSyntax newDisposeBody = existingDisposeMethod.Body.AddStatements(cleanupStatements ?? []); newDisposeMethod = existingDisposeMethod.WithBody(newDisposeBody); } else @@ -106,8 +137,24 @@ private static async Task AddDisposeAndBaseClassAsync( newDisposeMethod = existingDisposeMethod.WithBody(cleanupBody); } - editor.ReplaceNode(existingDisposeMethod, newDisposeMethod); - editor.RemoveNode(testCleanupMethod); + if (disposeDocument.Id == document.Id) + { + // Both methods are in the same document, use single editor + editor.ReplaceNode(existingDisposeMethod, newDisposeMethod); + editor.RemoveNode(testCleanupMethod); + solution = solution.WithDocumentSyntaxRoot(document.Id, editor.GetChangedRoot()); + } + else + { + // Methods are in different documents, use separate editors + DocumentEditor disposeEditor = await DocumentEditor.CreateAsync(disposeDocument, cancellationToken).ConfigureAwait(false); + disposeEditor.ReplaceNode(existingDisposeMethod, newDisposeMethod); + solution = solution.WithDocumentSyntaxRoot(disposeDocument.Id, disposeEditor.GetChangedRoot()); + + // Remove the TestCleanup method from the current document + editor.RemoveNode(testCleanupMethod); + solution = solution.WithDocumentSyntaxRoot(document.Id, editor.GetChangedRoot()); + } } else { @@ -123,9 +170,10 @@ private static async Task AddDisposeAndBaseClassAsync( } editor.ReplaceNode(containingType, newParent); + solution = solution.WithDocumentSyntaxRoot(document.Id, editor.GetChangedRoot()); } - return editor.GetChangedDocument(); + return solution; } private static bool ImplementsIDisposable(TypeDeclarationSyntax typeDeclaration, INamedTypeSymbol iDisposableSymbol, SemanticModel semanticModel) diff --git a/src/Analyzers/MSTest.Analyzers.CodeFixes/PreferTestInitializeOverConstructorFixer.cs b/src/Analyzers/MSTest.Analyzers.CodeFixes/PreferTestInitializeOverConstructorFixer.cs index 9ea85da311..5961aa1747 100644 --- a/src/Analyzers/MSTest.Analyzers.CodeFixes/PreferTestInitializeOverConstructorFixer.cs +++ b/src/Analyzers/MSTest.Analyzers.CodeFixes/PreferTestInitializeOverConstructorFixer.cs @@ -86,7 +86,7 @@ private static async Task ReplaceConstructorWithTestInitializeAsync(Do MethodDeclarationSyntax newTestInitialize; if (existingTestInitialize.Body != null) { - BlockSyntax newTestInitializeBody = existingTestInitialize.Body.AddStatements(constructorStatements ?? Array.Empty()); + BlockSyntax newTestInitializeBody = existingTestInitialize.Body.AddStatements(constructorStatements ?? []); newTestInitialize = existingTestInitialize.WithBody(newTestInitializeBody); } else diff --git a/src/Analyzers/MSTest.Analyzers.CodeFixes/PreferTestMethodOverDataTestMethodFixer.cs b/src/Analyzers/MSTest.Analyzers.CodeFixes/PreferTestMethodOverDataTestMethodFixer.cs new file mode 100644 index 0000000000..580a90cb3e --- /dev/null +++ b/src/Analyzers/MSTest.Analyzers.CodeFixes/PreferTestMethodOverDataTestMethodFixer.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.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 MSTest.Analyzers.Helpers; + +namespace MSTest.Analyzers; + +/// +/// Code fixer for . +/// +[ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(PreferTestMethodOverDataTestMethodFixer))] +[Shared] +public sealed class PreferTestMethodOverDataTestMethodFixer : CodeFixProvider +{ + /// + public override ImmutableArray FixableDiagnosticIds { get; } + = ImmutableArray.Create(DiagnosticIds.PreferTestMethodOverDataTestMethodRuleId); + + /// + public override FixAllProvider GetFixAllProvider() + => WellKnownFixAllProviders.BatchFixer; + + /// + public override async Task RegisterCodeFixesAsync(CodeFixContext context) + { + SyntaxNode root = await context.Document.GetRequiredSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); + + foreach (Diagnostic diagnostic in context.Diagnostics) + { + SyntaxNode? diagnosticNode = root?.FindNode(diagnostic.Location.SourceSpan); + if (diagnosticNode is not AttributeSyntax attributeSyntax) + { + continue; + } + + // Replace DataTestMethod with TestMethod + var action = CodeAction.Create( + title: CodeFixResources.ReplaceDataTestMethodWithTestMethodTitle, + createChangedDocument: c => Task.FromResult(ReplaceDataTestMethod(context.Document, root!, attributeSyntax)), + equivalenceKey: nameof(PreferTestMethodOverDataTestMethodFixer)); + + context.RegisterCodeFix(action, diagnostic); + } + } + + private static Document ReplaceDataTestMethod(Document document, SyntaxNode root, AttributeSyntax attributeSyntax) + { + AttributeSyntax newAttribute = attributeSyntax.WithName(SyntaxFactory.IdentifierName("TestMethod")); + SyntaxNode newRoot = root.ReplaceNode(attributeSyntax, newAttribute); + + return document.WithSyntaxRoot(newRoot); + } +} diff --git a/src/Analyzers/MSTest.Analyzers.CodeFixes/StringAssertToAssertFixer.cs b/src/Analyzers/MSTest.Analyzers.CodeFixes/StringAssertToAssertFixer.cs new file mode 100644 index 0000000000..b132e457a0 --- /dev/null +++ b/src/Analyzers/MSTest.Analyzers.CodeFixes/StringAssertToAssertFixer.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.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 MSTest.Analyzers.Helpers; + +namespace MSTest.Analyzers.CodeFixes; + +/// +/// Code fixer for . +/// +[ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(StringAssertToAssertFixer))] +[Shared] +public sealed class StringAssertToAssertFixer : CodeFixProvider +{ + /// + public override ImmutableArray FixableDiagnosticIds { get; } + = ImmutableArray.Create(DiagnosticIds.StringAssertToAssertRuleId); + + /// + public sealed 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); + + Diagnostic diagnostic = context.Diagnostics[0]; + if (!diagnostic.Properties.TryGetValue(StringAssertToAssertAnalyzer.ProperAssertMethodNameKey, out string? properAssertMethodName) + || properAssertMethodName == null) + { + return; + } + + if (root.FindNode(diagnostic.Location.SourceSpan, getInnermostNodeForTie: true) is not InvocationExpressionSyntax invocationExpressionSyntax) + { + return; + } + + // Register a code fix that will invoke the fix operation. + string title = string.Format(CultureInfo.InvariantCulture, CodeFixResources.StringAssertToAssertTitle, properAssertMethodName); + var action = CodeAction.Create( + title: title, + createChangedDocument: ct => FixStringAssertAsync(context.Document, invocationExpressionSyntax, properAssertMethodName, ct), + equivalenceKey: title); + + context.RegisterCodeFix(action, diagnostic); + } + + private static async Task FixStringAssertAsync( + Document document, + InvocationExpressionSyntax invocationExpr, + string properAssertMethodName, + CancellationToken cancellationToken) + { + // Check if the invocation expression has a member access expression + if (invocationExpr.Expression is not MemberAccessExpressionSyntax memberAccessExpr) + { + return document; + } + + SeparatedSyntaxList arguments = invocationExpr.ArgumentList.Arguments; + if (arguments.Count < 2) + { + return document; + } + + SyntaxNode root = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); + + // Create new argument list with swapped first two arguments + // We keep the existing separators in case there is trivia attached to them. + var newArguments = arguments.GetWithSeparators().ToList(); + // NOTE: Index 1 has the "separator" (comma) between the first and second arguments. + (newArguments[0], newArguments[2]) = (newArguments[2], newArguments[0]); + + // StringAssert has overloads with parameter types (string, string, string, StringComparison) + // In Assert class, these are (string, string, StringComparison, string) + // So we need to do the swap. + if (arguments.Count >= 4) + { + SemanticModel semanticModel = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); + if (semanticModel.GetSymbolInfo(invocationExpr, cancellationToken).Symbol is IMethodSymbol stringAssertMethod && + stringAssertMethod.Parameters.Length >= 4 && + stringAssertMethod.Parameters[2].Type.SpecialType == SpecialType.System_String && + stringAssertMethod.Parameters[3].Type.Name == "StringComparison") + { + (newArguments[4], newArguments[6]) = (newArguments[6], newArguments[4]); + } + } + + ArgumentListSyntax newArgumentList = invocationExpr.ArgumentList.WithArguments(SyntaxFactory.SeparatedList(newArguments)); + InvocationExpressionSyntax newInvocationExpr = invocationExpr.WithArgumentList(newArgumentList); + + // Replace StringAssert with Assert in the member access expression + // Change StringAssert.MethodName to Assert.ProperMethodName + MemberAccessExpressionSyntax newMemberAccess = memberAccessExpr.WithExpression(SyntaxFactory.IdentifierName("Assert")) + .WithName(SyntaxFactory.IdentifierName(properAssertMethodName)); + newInvocationExpr = newInvocationExpr.WithExpression(newMemberAccess); + + // Preserve leading trivia (including empty lines) from the original invocation + newInvocationExpr = newInvocationExpr.WithLeadingTrivia(invocationExpr.GetLeadingTrivia()); + + return document.WithSyntaxRoot(root.ReplaceNode(invocationExpr, newInvocationExpr)); + } +} diff --git a/src/Analyzers/MSTest.Analyzers.CodeFixes/TestClassShouldBeValidFixer.cs b/src/Analyzers/MSTest.Analyzers.CodeFixes/TestClassShouldBeValidFixer.cs index 1b593479a7..93580e61e1 100644 --- a/src/Analyzers/MSTest.Analyzers.CodeFixes/TestClassShouldBeValidFixer.cs +++ b/src/Analyzers/MSTest.Analyzers.CodeFixes/TestClassShouldBeValidFixer.cs @@ -9,7 +9,6 @@ using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; -using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Editing; using Microsoft.CodeAnalysis.Text; @@ -62,32 +61,24 @@ private static async Task FixClassDeclarationAsync(Document document, { cancellationToken.ThrowIfCancellationRequested(); - // Get the SemanticModel and Compilation + SyntaxNode root = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); SemanticModel semanticModel = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); bool canDiscoverInternals = semanticModel.Compilation.CanDiscoverInternals(); - DocumentEditor editor = await DocumentEditor.CreateAsync(document, cancellationToken).ConfigureAwait(false); + var generator = SyntaxGenerator.GetGenerator(document); - // Remove the static modifier if it exists - SyntaxTokenList modifiers = SyntaxFactory.TokenList( - typeDeclaration.Modifiers.Where(modifier => !modifier.IsKind(SyntaxKind.StaticKeyword))); + Accessibility existingAccessibility = generator.GetAccessibility(typeDeclaration); + bool isGoodAccessibility = existingAccessibility == Accessibility.Public || + (canDiscoverInternals && existingAccessibility == Accessibility.Internal); - if (!typeDeclaration.Modifiers.Any(SyntaxKind.PublicKeyword)) - { - // Determine the visibility modifier - SyntaxToken visibilityModifier = canDiscoverInternals - ? SyntaxFactory.Token(SyntaxKind.InternalKeyword) - : SyntaxFactory.Token(SyntaxKind.PublicKeyword); + SyntaxNode newTypeDeclaration = generator + .WithModifiers(typeDeclaration, generator.GetModifiers(typeDeclaration).WithIsStatic(false)); - modifiers = SyntaxFactory.TokenList( - modifiers.Where(modifier => !modifier.IsKind(SyntaxKind.PrivateKeyword) && !modifier.IsKind(SyntaxKind.InternalKeyword))).Add(visibilityModifier); + if (!isGoodAccessibility) + { + newTypeDeclaration = generator.WithAccessibility(newTypeDeclaration, Accessibility.Public); } - // Create a new class declaration with the updated modifiers. - TypeDeclarationSyntax newTypeDeclaration = typeDeclaration.WithModifiers(modifiers); - editor.ReplaceNode(typeDeclaration, newTypeDeclaration); - SyntaxNode newRoot = editor.GetChangedRoot(); - - return document.WithSyntaxRoot(newRoot); + return document.WithSyntaxRoot(root.ReplaceNode(typeDeclaration, newTypeDeclaration)); } } diff --git a/src/Analyzers/MSTest.Analyzers.CodeFixes/TestContextShouldBeValidFixer.cs b/src/Analyzers/MSTest.Analyzers.CodeFixes/TestContextShouldBeValidFixer.cs index c15765fa46..14639ce961 100644 --- a/src/Analyzers/MSTest.Analyzers.CodeFixes/TestContextShouldBeValidFixer.cs +++ b/src/Analyzers/MSTest.Analyzers.CodeFixes/TestContextShouldBeValidFixer.cs @@ -62,9 +62,6 @@ private static async Task FixMemberDeclarationAsync(Document document, { cancellationToken.ThrowIfCancellationRequested(); - // Get the SemanticModel and Compilation - SemanticModel semanticModel = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); - DocumentEditor editor = await DocumentEditor.CreateAsync(document, cancellationToken).ConfigureAwait(false); // Remove the static and readonly modifiers if it exists @@ -103,7 +100,7 @@ private static async Task FixMemberDeclarationAsync(Document document, AccessorDeclarationSyntax setAccessor = accessors.FirstOrDefault(a => a.Kind() == SyntaxKind.SetAccessorDeclaration) ?? SyntaxFactory.AccessorDeclaration(SyntaxKind.SetAccessorDeclaration).WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.SemicolonToken)); - newMemberDeclaration = propertyDeclaration.WithAccessorList(SyntaxFactory.AccessorList(SyntaxFactory.List(new[] { getAccessor, setAccessor }))); + newMemberDeclaration = propertyDeclaration.WithAccessorList(SyntaxFactory.AccessorList(SyntaxFactory.List([getAccessor, setAccessor]))); } // Create a new member declaration with the updated modifiers. @@ -121,13 +118,13 @@ private static PropertyDeclarationSyntax ConvertFieldToProperty(FieldDeclaration PropertyDeclarationSyntax propertyDeclaration = SyntaxFactory.PropertyDeclaration(type, TestContextShouldBeValidAnalyzer.TestContextPropertyName) .WithModifiers(SyntaxFactory.TokenList(fieldDeclaration.Modifiers)) .WithAccessorList(SyntaxFactory.AccessorList( - SyntaxFactory.List(new[] - { + SyntaxFactory.List( + [ SyntaxFactory.AccessorDeclaration(SyntaxKind.GetAccessorDeclaration) .WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.SemicolonToken)), SyntaxFactory.AccessorDeclaration(SyntaxKind.SetAccessorDeclaration) - .WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.SemicolonToken)), - }))); + .WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.SemicolonToken)) + ]))); return propertyDeclaration; } diff --git a/src/Analyzers/MSTest.Analyzers.CodeFixes/TestMethodShouldBeValidCodeFix.cs b/src/Analyzers/MSTest.Analyzers.CodeFixes/TestMethodShouldBeValidCodeFix.cs index 0facc4173f..263cfe53e2 100644 --- a/src/Analyzers/MSTest.Analyzers.CodeFixes/TestMethodShouldBeValidCodeFix.cs +++ b/src/Analyzers/MSTest.Analyzers.CodeFixes/TestMethodShouldBeValidCodeFix.cs @@ -93,20 +93,26 @@ private static async Task FixTestMethodAsync(Document document, Method SyntaxToken publicModifier = newMethodDeclaration.Modifiers.FirstOrDefault(m => m.IsKind(SyntaxKind.PublicKeyword)); if (publicModifier == default) { - newMethodDeclaration = newMethodDeclaration.WithModifiers(SyntaxFactory.TokenList(SyntaxFactory.Token(SyntaxKind.PublicKeyword))); + IEnumerable modifiersWithoutAccessModifiers = newMethodDeclaration.Modifiers.Where(m => + !m.IsKind(SyntaxKind.PrivateKeyword) && + !m.IsKind(SyntaxKind.ProtectedKeyword) && + !m.IsKind(SyntaxKind.InternalKeyword)); + + newMethodDeclaration = newMethodDeclaration.WithModifiers( + new SyntaxTokenList(SyntaxFactory.Token(SyntaxKind.PublicKeyword)) + .AddRange(modifiersWithoutAccessModifiers)); } // Ensure the method returns void or Task/ValueTask. SemanticModel semanticModel = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); - Compilation compilation = semanticModel.Compilation; var wellKnownTypeProvider = WellKnownTypeProvider.GetOrCreate(semanticModel.Compilation); INamedTypeSymbol? taskSymbol = wellKnownTypeProvider.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemThreadingTasksTask); INamedTypeSymbol? valueTaskSymbol = wellKnownTypeProvider.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemThreadingTasksValueTask); if (newMethodDeclaration.ReturnType != null && !newMethodDeclaration.ReturnType.IsVoid() && - (taskSymbol == null || !semanticModel.ClassifyConversion(newMethodDeclaration.ReturnType, taskSymbol).IsImplicit) && - (valueTaskSymbol == null || !semanticModel.ClassifyConversion(newMethodDeclaration.ReturnType, valueTaskSymbol).IsImplicit)) + (taskSymbol == null || !semanticModel.ClassifyConversion(methodDeclaration.ReturnType, taskSymbol).IsImplicit) && + (valueTaskSymbol == null || !semanticModel.ClassifyConversion(methodDeclaration.ReturnType, valueTaskSymbol).IsImplicit)) { // Change return type to void and remove return statements newMethodDeclaration = newMethodDeclaration.WithReturnType(SyntaxFactory.PredefinedType(SyntaxFactory.Token(SyntaxKind.VoidKeyword))); @@ -123,8 +129,8 @@ private static async Task FixTestMethodAsync(Document document, Method bool asyncModifier = newMethodDeclaration.Modifiers.Any(m => m.IsKind(SyntaxKind.AsyncKeyword)); if (asyncModifier && newMethodDeclaration.ReturnType != null && newMethodDeclaration.ReturnType.IsVoid()) { - // Change the return type to Task - newMethodDeclaration = newMethodDeclaration.WithReturnType(SyntaxFactory.ParseTypeName("Task ")); + // Change the return type to Task. We have a space after Task to ensure we have a trailing trivia (space) after Task. + newMethodDeclaration = newMethodDeclaration.WithReturnType(SyntaxFactory.IdentifierName("Task")); } // Apply changes. diff --git a/src/Analyzers/MSTest.Analyzers.CodeFixes/UseCancellationTokenPropertyFixer.cs b/src/Analyzers/MSTest.Analyzers.CodeFixes/UseCancellationTokenPropertyFixer.cs new file mode 100644 index 0000000000..f1f4238461 --- /dev/null +++ b/src/Analyzers/MSTest.Analyzers.CodeFixes/UseCancellationTokenPropertyFixer.cs @@ -0,0 +1,71 @@ +// 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 MSTest.Analyzers.Helpers; + +namespace MSTest.Analyzers; + +/// +/// Code fixer for . +/// +[ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(UseCancellationTokenPropertyFixer))] +[Shared] +public sealed class UseCancellationTokenPropertyFixer : CodeFixProvider +{ + /// + public sealed override ImmutableArray FixableDiagnosticIds { get; } + = ImmutableArray.Create(DiagnosticIds.UseCancellationTokenPropertyRuleId); + + /// + public override FixAllProvider? GetFixAllProvider() + => WellKnownFixAllProviders.BatchFixer; + + /// + public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) + { + SyntaxNode root = await context.Document.GetRequiredSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); + Diagnostic diagnostic = context.Diagnostics[0]; + + // The node here is a the node accessing CancellationTokenSource property on testContext. + SyntaxNode node = root.FindNode(diagnostic.Location.SourceSpan, getInnermostNodeForTie: true); + if (node is not MemberAccessExpressionSyntax memberAccessExpression) + { + return; + } + + // We are looking for testContext.CancellationTokenSource.Token. + // We already have testContext.CancellationTokenSource, so we get the parent, and check if it's accessing 'Token'. + if (memberAccessExpression.Parent is not MemberAccessExpressionSyntax parentMemberAccess || + parentMemberAccess.Name is not IdentifierNameSyntax { Identifier.ValueText: "Token" }) + { + return; + } + + context.RegisterCodeFix( + CodeAction.Create( + title: CodeFixResources.UseCancellationTokenPropertyFix, + createChangedDocument: c => + { + // Replace testContext.CancellationTokenSource.Token with testContext.CancellationToken + MemberAccessExpressionSyntax newExpression = SyntaxFactory.MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + memberAccessExpression.Expression, // testContext part + SyntaxFactory.IdentifierName("CancellationToken")); + + return Task.FromResult(context.Document.WithSyntaxRoot(root.ReplaceNode(parentMemberAccess, newExpression))); + }, + equivalenceKey: nameof(UseCancellationTokenPropertyFixer)), + diagnostic); + } +} diff --git a/src/Analyzers/MSTest.Analyzers.CodeFixes/UseCooperativeCancellationForTimeoutFixer.cs b/src/Analyzers/MSTest.Analyzers.CodeFixes/UseCooperativeCancellationForTimeoutFixer.cs new file mode 100644 index 0000000000..a3052a78f1 --- /dev/null +++ b/src/Analyzers/MSTest.Analyzers.CodeFixes/UseCooperativeCancellationForTimeoutFixer.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. + +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.Editing; +using Microsoft.CodeAnalysis.Text; + +using MSTest.Analyzers.Helpers; + +namespace MSTest.Analyzers; + +/// +/// Code fixer for . +/// +[ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(UseCooperativeCancellationForTimeoutFixer))] +[Shared] +public sealed class UseCooperativeCancellationForTimeoutFixer : CodeFixProvider +{ + /// + public sealed override ImmutableArray FixableDiagnosticIds { get; } + = ImmutableArray.Create(DiagnosticIds.UseCooperativeCancellationForTimeoutRuleId); + + /// + 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 sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) + { + SyntaxNode root = await context.Document.GetRequiredSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); + Diagnostic diagnostic = context.Diagnostics[0]; + TextSpan diagnosticSpan = diagnostic.Location.SourceSpan; + + // Find the attribute syntax node identified by the diagnostic + SyntaxNode attributeNode = root.FindNode(diagnosticSpan, getInnermostNodeForTie: true); + if (attributeNode is not AttributeSyntax attributeSyntax) + { + return; + } + + // Register a code action that will invoke the fix + context.RegisterCodeFix( + CodeAction.Create( + title: CodeFixResources.UseCooperativeCancellationForTimeoutFix, + createChangedDocument: c => AddCooperativeCancellationAsync(context.Document, attributeSyntax, c), + equivalenceKey: nameof(UseCooperativeCancellationForTimeoutFixer)), + diagnostic); + } + + private static async Task AddCooperativeCancellationAsync(Document document, AttributeSyntax attributeSyntax, CancellationToken cancellationToken) + { + DocumentEditor editor = await DocumentEditor.CreateAsync(document, cancellationToken).ConfigureAwait(false); + + AttributeSyntax newAttributeSyntax; + + if (attributeSyntax.ArgumentList == null) + { + // No argument list exists, create one with CooperativeCancellation = true + AttributeArgumentSyntax cooperativeCancellationArg = SyntaxFactory.AttributeArgument( + SyntaxFactory.NameEquals("CooperativeCancellation"), + null, + SyntaxFactory.LiteralExpression(SyntaxKind.TrueLiteralExpression)); + + newAttributeSyntax = attributeSyntax.WithArgumentList( + SyntaxFactory.AttributeArgumentList( + SyntaxFactory.SingletonSeparatedList(cooperativeCancellationArg))); + } + else + { + // Argument list exists, check if CooperativeCancellation is already specified + bool hasCooperativeCancellation = false; + List newArguments = []; + + foreach (AttributeArgumentSyntax arg in attributeSyntax.ArgumentList.Arguments) + { + if (arg.NameEquals?.Name.Identifier.ValueText == "CooperativeCancellation") + { + // Replace existing CooperativeCancellation = false with true + hasCooperativeCancellation = true; + AttributeArgumentSyntax newArg = arg.WithExpression( + SyntaxFactory.LiteralExpression(SyntaxKind.TrueLiteralExpression)); + newArguments.Add(newArg); + } + else + { + newArguments.Add(arg); + } + } + + if (!hasCooperativeCancellation) + { + // Add CooperativeCancellation = true to existing arguments + AttributeArgumentSyntax cooperativeCancellationArg = SyntaxFactory.AttributeArgument( + SyntaxFactory.NameEquals("CooperativeCancellation"), + null, + SyntaxFactory.LiteralExpression(SyntaxKind.TrueLiteralExpression)); + + newArguments.Add(cooperativeCancellationArg); + } + + newAttributeSyntax = attributeSyntax.WithArgumentList( + attributeSyntax.ArgumentList.WithArguments( + SyntaxFactory.SeparatedList(newArguments))); + } + + // Replace the old attribute with the new one + editor.ReplaceNode(attributeSyntax, newAttributeSyntax); + + return editor.GetChangedDocument(); + } +} diff --git a/src/Analyzers/MSTest.Analyzers.CodeFixes/UseProperAssertMethodsFixer.cs b/src/Analyzers/MSTest.Analyzers.CodeFixes/UseProperAssertMethodsFixer.cs index 37be3ecc36..a2b22ce340 100644 --- a/src/Analyzers/MSTest.Analyzers.CodeFixes/UseProperAssertMethodsFixer.cs +++ b/src/Analyzers/MSTest.Analyzers.CodeFixes/UseProperAssertMethodsFixer.cs @@ -70,7 +70,7 @@ public override async Task RegisterCodeFixesAsync(CodeFixContext context) switch (mode) { case UseProperAssertMethodsAnalyzer.CodeFixModeSimple: - createChangedDocument = ct => FixAssertMethodForSimpleModeAsync(context.Document, diagnostic.AdditionalLocations[0], diagnostic.AdditionalLocations[1], root, simpleNameSyntax, properAssertMethodName, ct); + createChangedDocument = ct => FixAssertMethodForSimpleModeAsync(context.Document, diagnostic.AdditionalLocations, root, simpleNameSyntax, properAssertMethodName, ct); break; case UseProperAssertMethodsAnalyzer.CodeFixModeAddArgument: createChangedDocument = ct => FixAssertMethodForAddArgumentModeAsync(context.Document, diagnostic.AdditionalLocations[0], diagnostic.AdditionalLocations[1], diagnostic.AdditionalLocations[2], root, simpleNameSyntax, properAssertMethodName, ct); @@ -78,6 +78,9 @@ public override async Task RegisterCodeFixesAsync(CodeFixContext context) case UseProperAssertMethodsAnalyzer.CodeFixModeRemoveArgument: createChangedDocument = ct => FixAssertMethodForRemoveArgumentModeAsync(context.Document, diagnostic.AdditionalLocations, root, simpleNameSyntax, properAssertMethodName, diagnostic.Properties.ContainsKey(UseProperAssertMethodsAnalyzer.NeedsNullableBooleanCastKey), ct); break; + case UseProperAssertMethodsAnalyzer.CodeFixModeRemoveArgumentAndReplaceArgument: + createChangedDocument = ct => FixAssertMethodForRemoveArgumentAndReplaceArgumentModeAsync(context.Document, diagnostic.AdditionalLocations, root, simpleNameSyntax, properAssertMethodName, ct); + break; default: break; } @@ -93,26 +96,30 @@ public override async Task RegisterCodeFixesAsync(CodeFixContext context) } } - private static async Task FixAssertMethodForSimpleModeAsync(Document document, Location conditionLocationToBeReplaced, Location replacementExpressionLocation, SyntaxNode root, SimpleNameSyntax simpleNameSyntax, string properAssertMethodName, CancellationToken cancellationToken) + private static async Task FixAssertMethodForSimpleModeAsync(Document document, IReadOnlyList additionalLocations, SyntaxNode root, SimpleNameSyntax simpleNameSyntax, string properAssertMethodName, CancellationToken cancellationToken) { - // This doesn't properly handle cases like Assert.IsTrue(message: "My message", condition: x == null) - // The proper handling of this may be Assert.IsNull(message: "My message", value: x) - // Or: Assert.IsNull(x, "My message") - // For now this is not handled. - if (root.FindNode(conditionLocationToBeReplaced.SourceSpan) is not ArgumentSyntax conditionNodeToBeReplaced) - { - return document; - } + DocumentEditor editor = await DocumentEditor.CreateAsync(document, cancellationToken).ConfigureAwait(false); + FixInvocationMethodName(editor, simpleNameSyntax, properAssertMethodName); - if (root.FindNode(replacementExpressionLocation.SourceSpan) is not ExpressionSyntax replacementExpressionNode) + for (int i = 0; i < additionalLocations.Count; i += 2) { - return document; + // This doesn't properly handle cases like Assert.IsTrue(message: "My message", condition: x == null) + // The proper handling of this may be Assert.IsNull(message: "My message", value: x) + // Or: Assert.IsNull(x, "My message") + // For now this is not handled. + if (root.FindNode(additionalLocations[i].SourceSpan) is not ArgumentSyntax conditionNodeToBeReplaced) + { + return document; + } + + if (root.FindNode(additionalLocations[i + 1].SourceSpan, getInnermostNodeForTie: true) is not ExpressionSyntax replacementExpressionNode) + { + return document; + } + + editor.ReplaceNode(conditionNodeToBeReplaced, SyntaxFactory.Argument(replacementExpressionNode).WithAdditionalAnnotations(Formatter.Annotation)); } - DocumentEditor editor = await DocumentEditor.CreateAsync(document, cancellationToken).ConfigureAwait(false); - FixInvocationMethodName(editor, simpleNameSyntax, properAssertMethodName); - editor.ReplaceNode(conditionNodeToBeReplaced, SyntaxFactory.Argument(replacementExpressionNode).WithAdditionalAnnotations(Formatter.Annotation)); - return editor.GetChangedDocument(); } @@ -132,7 +139,8 @@ private static async Task FixAssertMethodForAddArgumentModeAsync(Docum return document; } - if (root.FindNode(expectedLocation.SourceSpan) is not ExpressionSyntax expectedNode) + if (root.FindNode(expectedLocation.SourceSpan) is not { } expectedNode + || expectedNode is not ArgumentSyntax and not ExpressionSyntax) { return document; } @@ -146,7 +154,13 @@ private static async Task FixAssertMethodForAddArgumentModeAsync(Docum FixInvocationMethodName(editor, simpleNameSyntax, properAssertMethodName); ArgumentListSyntax newArgumentList = argumentList; - newArgumentList = newArgumentList.ReplaceNode(conditionNode, SyntaxFactory.Argument(expectedNode).WithAdditionalAnnotations(Formatter.Annotation)); + ExpressionSyntax expectedExpression = expectedNode switch + { + ArgumentSyntax argument => argument.Expression, + ExpressionSyntax expression => expression, + _ => throw new InvalidOperationException($"Unexpected node type for expected argument: {expectedNode.GetType()}"), + }; + newArgumentList = newArgumentList.ReplaceNode(conditionNode, SyntaxFactory.Argument(expectedExpression).WithAdditionalAnnotations(Formatter.Annotation)); int insertionIndex = argumentList.Arguments.IndexOf(conditionNode) + 1; newArgumentList = newArgumentList.WithArguments(newArgumentList.Arguments.Insert(insertionIndex, SyntaxFactory.Argument(actualNode).WithAdditionalAnnotations(Formatter.Annotation))); @@ -203,6 +217,44 @@ private static async Task FixAssertMethodForRemoveArgumentModeAsync( return editor.GetChangedDocument(); } + private static async Task FixAssertMethodForRemoveArgumentAndReplaceArgumentModeAsync( + Document document, + IReadOnlyList additionalLocations, + SyntaxNode root, + SimpleNameSyntax simpleNameSyntax, + string properAssertMethodName, + CancellationToken cancellationToken) + { + // Handle collection count transformations: + // Assert.AreEqual(0, list.Count) -> Assert.IsEmpty(list) + // Assert.AreEqual(list.Count, 0) -> Assert.IsEmpty(list) + if (root.FindNode(additionalLocations[0].SourceSpan) is not ArgumentSyntax expectedArgumentToRemove) + { + return document; + } + + if (root.FindNode(additionalLocations[1].SourceSpan) is not ArgumentSyntax argumentToBeReplaced || + root.FindNode(additionalLocations[2].SourceSpan) is not ExpressionSyntax replacement) + { + return document; + } + + if (expectedArgumentToRemove.Parent is not ArgumentListSyntax argumentList) + { + return document; + } + + DocumentEditor editor = await DocumentEditor.CreateAsync(document, cancellationToken).ConfigureAwait(false); + FixInvocationMethodName(editor, simpleNameSyntax, properAssertMethodName); + + int argumentIndexToRemove = argumentList.Arguments.IndexOf(expectedArgumentToRemove); + ArgumentListSyntax newArgumentList = argumentList.ReplaceNode(argumentToBeReplaced, argumentToBeReplaced.WithExpression(replacement)); + newArgumentList = newArgumentList.WithArguments(newArgumentList.Arguments.RemoveAt(argumentIndexToRemove)); + editor.ReplaceNode(argumentList, newArgumentList); + + return editor.GetChangedDocument(); + } + private static void FixInvocationMethodName(DocumentEditor editor, SimpleNameSyntax simpleNameSyntax, string properAssertMethodName) // NOTE: Switching Assert.IsTrue(x == y) to Assert.AreEqual(x, y) MAY produce an overload resolution error. // For example, Assert.AreEqual("string", true) will fail the inference for generic argument. diff --git a/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.cs.xlf b/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.cs.xlf index 5e01c72cca..cc2d8dc45e 100644 --- a/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.cs.xlf +++ b/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.cs.xlf @@ -22,11 +22,26 @@ Použít {0} + + Use interpolated string instead of format parameters + Místo parametrů formátu použít interpolovaný řetězec + + + + Use 'string.Format' instead of format parameters + Místo parametrů formátu použít argument string.Format + + Change method accessibility to 'private' Změnit přístupnost metody na private + + Change to 'class' and add '[TestClass]' + Změňte na class a přidejte [TestClass] + + Fix actual/expected arguments order Opravit pořadí skutečných/očekávaných argumentů @@ -42,6 +57,21 @@ Opravit podpis + + Pass 'TestContext.CancellationToken' argument to method call + Předat argument TestContext.CancellationToken volání metody + + + + Remove 'DynamicDataSourceType' parameter to use default 'AutoDetect' + Pokud chcete použít výchozí AutoDetect, odeberte parametr DynamicDataSourceType. + + + + Replace 'DataTestMethod' with 'TestMethod' + Nahradit DataTestMethod hodnotou TestMethod + + Replace TestInitialize method with constructor Nahradit metodu TestInitialize konstruktorem @@ -67,6 +97,11 @@ Nahradit konstruktor metodou TestInitialize + + Use 'Assert.{0}' instead of 'StringAssert' + Použijte Assert.{0}místo StringAssert + + Fix test class signature Oprava podpisu testovací třídy @@ -92,6 +127,16 @@ Přidat [TestMethod] + + Use 'TestContext.CancellationToken' instead + Místo toho použít argument TestContext.CancellationToken + + + + Use 'CooperativeCancellation = true' + Použijte „CooperativeCancellation = true“ + + Use '{0}' Použít {0} diff --git a/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.de.xlf b/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.de.xlf index ecef33e935..6a3d19f1bc 100644 --- a/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.de.xlf +++ b/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.de.xlf @@ -22,11 +22,26 @@ „{0}“ verwenden + + Use interpolated string instead of format parameters + Interpolierte Zeichenfolge anstelle von Formatparametern verwenden + + + + Use 'string.Format' instead of format parameters + „string.Format“ anstelle von Formatparametern verwenden + + Change method accessibility to 'private' Methodenzugriff auf „privat“ ändern + + Change to 'class' and add '[TestClass]' + Wechseln Sie zu „Klasse“, und fügen Sie „[Testklasse]“ hinzu + + Fix actual/expected arguments order Reihenfolge der tatsächlichen/erwarteten Argumente korrigieren @@ -42,6 +57,21 @@ Signatur korrigieren + + Pass 'TestContext.CancellationToken' argument to method call + Argument „TestContext.CancellationToken“ an den Methodenaufruf übergeben + + + + Remove 'DynamicDataSourceType' parameter to use default 'AutoDetect' + Entfernen Sie den Parameter „DynamicDataSourceType“, um die Standardeinstellung „AutoDetect“ zu verwenden. + + + + Replace 'DataTestMethod' with 'TestMethod' + „DataTestMethod“ durch „TestMethod“ ersetzen + + Replace TestInitialize method with constructor TestInitialize-Methode durch Konstruktor ersetzen @@ -67,6 +97,11 @@ Konstruktor durch TestInitialize-Methode ersetzen + + Use 'Assert.{0}' instead of 'StringAssert' + Verwenden Sie „Assert.{0}“ anstelle von „StringAssert“. + + Fix test class signature Testklassensignatur korrigieren @@ -92,6 +127,16 @@ „[TestMethod]“ hinzufügen + + Use 'TestContext.CancellationToken' instead + Stattdessen „TestContext.CancellationToken“ verwenden + + + + Use 'CooperativeCancellation = true' + Verwenden Sie „CooperativeCancellation = true“. + + Use '{0}' „{0}“ verwenden diff --git a/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.es.xlf b/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.es.xlf index 1edccf3e61..e285a57aa0 100644 --- a/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.es.xlf +++ b/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.es.xlf @@ -22,11 +22,26 @@ Usar "{0}" + + Use interpolated string instead of format parameters + Usar cadena interpolada en lugar de parámetros de formato + + + + Use 'string.Format' instead of format parameters + Usar 'string. Format' en lugar de parámetros de formato + + Change method accessibility to 'private' Cambiar la accesibilidad del método a "private" + + Change to 'class' and add '[TestClass]' + Cambiar a "class" y agregar "[TestClass]" + + Fix actual/expected arguments order Corregir el orden de los argumentos reales o esperados @@ -42,6 +57,21 @@ Corregir firma + + Pass 'TestContext.CancellationToken' argument to method call + Pasar el argumento 'TestContext.CancellationToken' a la llamada al método + + + + Remove 'DynamicDataSourceType' parameter to use default 'AutoDetect' + Elimine el parámetro 'DynamicDataSourceType' para utilizar el valor predeterminado 'AutoDetect' + + + + Replace 'DataTestMethod' with 'TestMethod' + Reemplazar "DataTestMethod" por "TestMethod" + + Replace TestInitialize method with constructor Reemplazar el método TestInitialize por el constructor @@ -67,6 +97,11 @@ Reemplazar constructor por el método TestInitialize + + Use 'Assert.{0}' instead of 'StringAssert' + Use "Assert.{0}" en lugar de "StringAssert" + + Fix test class signature Corregir firma de clase de prueba @@ -92,6 +127,16 @@ Agregar '[TestMethod]' + + Use 'TestContext.CancellationToken' instead + Usar 'TestContext.CancellationToken' en su lugar + + + + Use 'CooperativeCancellation = true' + Usa "CooperativeCancellation = true" + + Use '{0}' Usar "{0}" diff --git a/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.fr.xlf b/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.fr.xlf index ed28b55f45..142da1b150 100644 --- a/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.fr.xlf +++ b/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.fr.xlf @@ -22,11 +22,26 @@ Utiliser « {0} » + + Use interpolated string instead of format parameters + Utiliser une chaîne interpolée au lieu de paramètres de format + + + + Use 'string.Format' instead of format parameters + Utiliser « string.Format » au lieu des paramètres de format + + Change method accessibility to 'private' Remplacer l’accessibilité de la méthode par « privé » + + Change to 'class' and add '[TestClass]' + Remplacez par « classe » et ajoutez « [TestClass] » + + Fix actual/expected arguments order Corriger l’ordre des arguments réels/attendus @@ -42,6 +57,21 @@ Corriger la signature numérique + + Pass 'TestContext.CancellationToken' argument to method call + Transmettre l'argument « TestContext.CancellationToken » à l'appel de méthode + + + + Remove 'DynamicDataSourceType' parameter to use default 'AutoDetect' + Pour utiliser la valeur par défaut « AutoDetect », supprimez le paramètre « DynamicDataSourceType » + + + + Replace 'DataTestMethod' with 'TestMethod' + Remplacer « DataTestMethod » par « TestMethod » + + Replace TestInitialize method with constructor Remplacer la méthode TestInitialize par un constructeur @@ -67,6 +97,11 @@ Remplacer le constructeur par la méthode TestInitialize + + Use 'Assert.{0}' instead of 'StringAssert' + Utilisez « Assert.{0} » au lieu de « StringAssert » + + Fix test class signature Correction de la signature de classe de test @@ -92,6 +127,16 @@ Ajouter « [TestMethod] » + + Use 'TestContext.CancellationToken' instead + Utiliser plutôt « TestContext.CancellationToken » + + + + Use 'CooperativeCancellation = true' + Utilisez 'CooperativeCancellation = true' + + Use '{0}' Utiliser « {0} » diff --git a/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.it.xlf b/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.it.xlf index 10aafdcc89..c00367afaf 100644 --- a/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.it.xlf +++ b/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.it.xlf @@ -22,11 +22,26 @@ Usare ‘{0}’ + + Use interpolated string instead of format parameters + Usare la stringa interpolata invece dei parametri di formato + + + + Use 'string.Format' instead of format parameters + Usare 'string. Format' invece dei parametri di formato + + Change method accessibility to 'private' Modifica l'accessibilità del metodo in 'privato' + + Change to 'class' and add '[TestClass]' + Cambia in 'class' e aggiungi '[TestClass]' + + Fix actual/expected arguments order Correggi l'ordine degli argomenti effettivi/previsti @@ -42,6 +57,21 @@ Correggi firma + + Pass 'TestContext.CancellationToken' argument to method call + Passare l'argomento 'TestContext.CancellationToken' alla chiamata al metodo + + + + Remove 'DynamicDataSourceType' parameter to use default 'AutoDetect' + Rimuovi il parametro ''DynamicDataSourceType'' per utilizzare il valore predefinito ''AutoDetect'' + + + + Replace 'DataTestMethod' with 'TestMethod' + Sostituisci 'DataTestMethod' con 'TestMethod' + + Replace TestInitialize method with constructor Sostituisci il metodo TestInitialize con il costruttore @@ -67,6 +97,11 @@ Sostituisci costruttore con metodo TestInitialize + + Use 'Assert.{0}' instead of 'StringAssert' + Usa 'Assert.{0}' invece di 'StringAssert' + + Fix test class signature Correggi la firma della classe di test @@ -92,6 +127,16 @@ Aggiungi '[TestMethod]' + + Use 'TestContext.CancellationToken' instead + In alternativa, usare 'TestContext.CancellationToken' + + + + Use 'CooperativeCancellation = true' + Usa "CooperativeCancellation = true" + + Use '{0}' Usare ‘{0}’ diff --git a/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.ja.xlf b/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.ja.xlf index ad8e53d586..7d7b217dee 100644 --- a/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.ja.xlf +++ b/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.ja.xlf @@ -22,11 +22,26 @@ '{0}' を使用する + + Use interpolated string instead of format parameters + 書式設定パラメーターの代わりに補間された文字列を使用する + + + + Use 'string.Format' instead of format parameters + 書式設定パラメーターの代わりに 'string.Format' を使用する + + Change method accessibility to 'private' メソッドのアクセシビリティを 'private' に変更する + + Change to 'class' and add '[TestClass]' + 'class' に変更し、'[TestClass]' を追加します + + Fix actual/expected arguments order 実際の引数と予想される引数の順序を修正する @@ -42,6 +57,21 @@ 署名の修正 + + Pass 'TestContext.CancellationToken' argument to method call + 'TestContext.CancellationToken' 引数をメソッド呼び出しに渡す + + + + Remove 'DynamicDataSourceType' parameter to use default 'AutoDetect' + 'DynamicDataSourceType' パラメーターを削除して、既定の 'AutoDetect' を使用します + + + + Replace 'DataTestMethod' with 'TestMethod' + 'DataTestMethod' を 'TestMethod' に置き換えます + + Replace TestInitialize method with constructor TestInitialize メソッドをコンストラクターに置き換える @@ -67,6 +97,11 @@ コンストラクターを TestInitialize メソッドに置き換える + + Use 'Assert.{0}' instead of 'StringAssert' + 'StringAssert' の代わりに 'Assert.{0}' を使用 + + Fix test class signature テスト クラスのシグネチャの修正 @@ -92,6 +127,16 @@ '[TestMethod]' の追加 + + Use 'TestContext.CancellationToken' instead + 代わりに 'TestContext.CancellationToken' を使用する + + + + Use 'CooperativeCancellation = true' + 'CooperativeCancellation = true' を使用する + + Use '{0}' '{0}' を使用する diff --git a/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.ko.xlf b/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.ko.xlf index 9d32dc54eb..c3457804a2 100644 --- a/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.ko.xlf +++ b/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.ko.xlf @@ -22,11 +22,26 @@ '{0}' 사용 + + Use interpolated string instead of format parameters + 형식 매개 변수 대신 보간된 문자열 사용 + + + + Use 'string.Format' instead of format parameters + 형식 매개 변수 대신 'string.Format' 사용 + + Change method accessibility to 'private' 메서드 접근성 '비공개'로 변경하기 + + Change to 'class' and add '[TestClass]' + 'class'로 변경하고 '[TestClass]'를 추가합니다. + + Fix actual/expected arguments order 실제/예상 인수 순서 수정 @@ -42,6 +57,21 @@ 서명 수정 + + Pass 'TestContext.CancellationToken' argument to method call + 메서드 호출에 'TestContext.CancellationToken' 인수 전달 + + + + Remove 'DynamicDataSourceType' parameter to use default 'AutoDetect' + 기본 'AutoDetect'를 사용하려면 'DynamicDataSourceType' 매개 변수를 제거합니다. + + + + Replace 'DataTestMethod' with 'TestMethod' + 'DataTestMethod'를 'TestMethod'로 바꾸기 + + Replace TestInitialize method with constructor TestInitialize 메서드를 생성자로 바꾸기 @@ -67,6 +97,11 @@ 생성자를 TestInitialize 메서드로 바꾸기 + + Use 'Assert.{0}' instead of 'StringAssert' + 'StringAssert' 대신 'Assert.{0}' 사용 + + Fix test class signature 테스트 클래스 서명 수정 @@ -92,6 +127,16 @@ '[TestMethod]' 추가 + + Use 'TestContext.CancellationToken' instead + 대신 'TestContext.CancellationToken' 사용 + + + + Use 'CooperativeCancellation = true' + 'CooperativeCancellation = true'를 사용하세요. + + Use '{0}' '{0}' 사용 diff --git a/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.pl.xlf b/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.pl.xlf index d879b5a155..9e51816347 100644 --- a/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.pl.xlf +++ b/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.pl.xlf @@ -22,11 +22,26 @@ Użyj „{0}” + + Use interpolated string instead of format parameters + Użyj ciągu interpolowanego zamiast parametrów formatu + + + + Use 'string.Format' instead of format parameters + Użyj ciągu „string.Format” zamiast parametrów formatu + + Change method accessibility to 'private' Zmień dostępność metody na „private” (prywatna) + + Change to 'class' and add '[TestClass]' + Zmień na wartość „class” i dodaj element „[TestClass]” + + Fix actual/expected arguments order Napraw rzeczywistą/oczekiwaną kolejność argumentów @@ -42,6 +57,21 @@ Popraw podpis + + Pass 'TestContext.CancellationToken' argument to method call + Przekaż argument „TestContext.CancellationToken” do wywołania metody + + + + Remove 'DynamicDataSourceType' parameter to use default 'AutoDetect' + Usuń parametr „DynamicDataSourceType”, aby użyć domyślnego elementu „AutoDetect” + + + + Replace 'DataTestMethod' with 'TestMethod' + Zastąp element „DataTestMethod” elementem „TestMethod” + + Replace TestInitialize method with constructor Zastąp metodę TestInitialize konstruktorem @@ -67,6 +97,11 @@ Zastąp konstruktor metodą TestInitialize + + Use 'Assert.{0}' instead of 'StringAssert' + Użyj ciągu „Assert.{0}” zamiast ciągu „StringAssert” + + Fix test class signature Napraw podpis klasy testowej @@ -92,6 +127,16 @@ Dodaj „[TestMethod]” + + Use 'TestContext.CancellationToken' instead + Zamiast tego użyj argumentu „TestContext.CancellationToken” + + + + Use 'CooperativeCancellation = true' + Użyj opcji „CooperativeCancellation = true” + + Use '{0}' Użyj „{0}” 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 646745d87a..509eb8d8d4 100644 --- a/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.pt-BR.xlf +++ b/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.pt-BR.xlf @@ -22,11 +22,26 @@ Usar '{0}' + + Use interpolated string instead of format parameters + Use cadeia de caracteres interpolada em vez de parâmetros de formato + + + + Use 'string.Format' instead of format parameters + Use 'string.Format' em vez de parâmetros de formato + + Change method accessibility to 'private' Alterar a acessibilidade do método para 'privado' + + Change to 'class' and add '[TestClass]' + Alterar para 'classe' e adicionar '[TestClass]' + + Fix actual/expected arguments order Corrigir ordem de argumentos real/esperada @@ -42,6 +57,21 @@ Corrigir assinatura + + Pass 'TestContext.CancellationToken' argument to method call + Passar o argumento 'TestContext.CancellationToken' para a chamada de método + + + + Remove 'DynamicDataSourceType' parameter to use default 'AutoDetect' + Remover o parâmetro "DynamicDataSourceType" para usar o padrão "AutoDetect" + + + + Replace 'DataTestMethod' with 'TestMethod' + Substitua 'DataTestMethod' por 'TestMethod' + + Replace TestInitialize method with constructor Substituir o método TestInitialize pelo construtor. @@ -67,6 +97,11 @@ Substitua o construtor pelo método TestInitialize. + + Use 'Assert.{0}' instead of 'StringAssert' + Use 'Assert'{0}. em vez de 'StringAssert' + + Fix test class signature Correção da assinatura de classe do teste @@ -92,6 +127,16 @@ Adicionar ''[TestMethod]" + + Use 'TestContext.CancellationToken' instead + Usar 'TestContext.CancellationToken' em vez disso + + + + Use 'CooperativeCancellation = true' + Usar “CooperativoCancellation = true” + + Use '{0}' Usar "{0}" diff --git a/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.ru.xlf b/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.ru.xlf index d2e9dbd136..612dc20fd7 100644 --- a/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.ru.xlf +++ b/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.ru.xlf @@ -22,11 +22,26 @@ Использовать "{0}" + + Use interpolated string instead of format parameters + Используйте интерполированную строку вместо параметров форматирования + + + + Use 'string.Format' instead of format parameters + Используйте "string.Format" вместо параметров форматирования + + Change method accessibility to 'private' Изменить доступность метода на "private" + + Change to 'class' and add '[TestClass]' + Изменить на "class" и добавить "[TestClass]" + + Fix actual/expected arguments order Исправить порядок фактических и ожидаемых аргументов @@ -42,6 +57,21 @@ Исправить подпись + + Pass 'TestContext.CancellationToken' argument to method call + Передайте аргумент "TestContext.CancellationToken" в вызов метода + + + + Remove 'DynamicDataSourceType' parameter to use default 'AutoDetect' + Удалите параметр "DynamicDataSourceType", чтобы использовать значение по умолчанию "AutoDetect" + + + + Replace 'DataTestMethod' with 'TestMethod' + Заменить "DataTestMethod" на "TestMethod" + + Replace TestInitialize method with constructor Заменить метод TestInitialize конструктором @@ -67,6 +97,11 @@ Заменить конструктор на метод TestInitialize + + Use 'Assert.{0}' instead of 'StringAssert' + Используйте "Assert.{0}" вместо "StringAssert" + + Fix test class signature Исправить подпись класса теста @@ -92,6 +127,16 @@ Добавить "[TestMethod]" + + Use 'TestContext.CancellationToken' instead + Вместо этого используйте "TestContext.CancellationToken" + + + + Use 'CooperativeCancellation = true' + Использовать "CooperativeCancellation = true" + + Use '{0}' Использовать "{0}" diff --git a/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.tr.xlf b/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.tr.xlf index 48e1032eb5..9d31daafe5 100644 --- a/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.tr.xlf +++ b/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.tr.xlf @@ -22,11 +22,26 @@ '{0}' kullan + + Use interpolated string instead of format parameters + Biçim parametreleri yerine düz metin arasına kod eklenmiş dize kullanın + + + + Use 'string.Format' instead of format parameters + Biçim parametreleri yerine 'string.Format' kullanın + + Change method accessibility to 'private' Yöntem erişilebilirliğini ‘özel’ olarak değiştir + + Change to 'class' and add '[TestClass]' + ‘class’ olarak değiştirin ve ‘[TestClass]’ ekleyin. + + Fix actual/expected arguments order Fiili/beklenen bağımsız değişken sırasını düzelt @@ -42,6 +57,21 @@ İmzayı düzelt + + Pass 'TestContext.CancellationToken' argument to method call + 'TestContext.CancellationToken' bağımsız değişkenini metot çağrısına aktarın + + + + Remove 'DynamicDataSourceType' parameter to use default 'AutoDetect' + Varsayılan ‘AutoDetect’ değerini kullanmak için ‘DynamicDataSourceType’ parametresini kaldırın + + + + Replace 'DataTestMethod' with 'TestMethod' + 'DataTestMethod' yöntemini 'TestMethod' ile değiştirin + + Replace TestInitialize method with constructor TestInitialize yöntemini oluşturucuyla değiştir @@ -67,6 +97,11 @@ Oluşturucuyu TestInitialize yöntemiyle değiştir + + Use 'Assert.{0}' instead of 'StringAssert' + ‘StringAssert’ yerine ‘Assert.{0}’ kullanın + + Fix test class signature Test sınıfı imzasını düzeltme @@ -92,6 +127,16 @@ '[TestMethod]' ekle + + Use 'TestContext.CancellationToken' instead + Bunun yerine 'TestContext.CancellationToken' kullanın + + + + Use 'CooperativeCancellation = true' + 'CooperativeCancellation = true' kullanın + + Use '{0}' '{0}' kullan 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 2f4b66073d..97c4645e47 100644 --- a/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.zh-Hans.xlf +++ b/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.zh-Hans.xlf @@ -22,11 +22,26 @@ 使用“{0}” + + Use interpolated string instead of format parameters + 使用内插字符串而不是格式参数 + + + + Use 'string.Format' instead of format parameters + 使用 'string.Format' 而不是格式参数 + + Change method accessibility to 'private' 将方法可访问性更改为“private” + + Change to 'class' and add '[TestClass]' + 更改为 "class" 并添加 "[TestClass]" + + Fix actual/expected arguments order 修复实际/预期参数顺序 @@ -42,6 +57,21 @@ 修复签名 + + Pass 'TestContext.CancellationToken' argument to method call + 将 'TestContext.CancellationToken' 参数传递给方法调用 + + + + Remove 'DynamicDataSourceType' parameter to use default 'AutoDetect' + 移除 ‘DynamicDataSourceType’ 参数以使用默认的 ‘AutoDetect’ + + + + Replace 'DataTestMethod' with 'TestMethod' + 将 'DataTestMethod' 替换为 'TestMethod' + + Replace TestInitialize method with constructor 将 TestInitialize 方法替换为构造函数 @@ -67,6 +97,11 @@ 将构造函数替换为 TestInitialize 方法 + + Use 'Assert.{0}' instead of 'StringAssert' + 使用 'Assert.{0}' 而不是 'StringAssert' + + Fix test class signature 修复测试类签名 @@ -92,6 +127,16 @@ 添加“[TestMethod]” + + Use 'TestContext.CancellationToken' instead + 请改用 'TestContext.CancellationToken' + + + + Use 'CooperativeCancellation = true' + 使用 'CooperativeCancellation = true' + + Use '{0}' 使用“{0}” 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 5c5ea7e81d..67a726771e 100644 --- a/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.zh-Hant.xlf +++ b/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.zh-Hant.xlf @@ -22,11 +22,26 @@ 使用 '{0}' + + Use interpolated string instead of format parameters + 請使用差補字串而不是格式參數 + + + + Use 'string.Format' instead of format parameters + 請使用 'string.Format' 而不是格式參數 + + Change method accessibility to 'private' 將方法協助工具變更為 'private' + + Change to 'class' and add '[TestClass]' + 變更為 'class',並新增 '[TestClass]' + + Fix actual/expected arguments order 修正實際/預期的引數順序 @@ -42,6 +57,21 @@ 修正簽章 + + Pass 'TestContext.CancellationToken' argument to method call + 將 'TestContext.CancellationToken' 引數傳遞給方法呼叫 + + + + Remove 'DynamicDataSourceType' parameter to use default 'AutoDetect' + 移除 'DynamicDataSourceType' 參數,以使用預設的 'AutoDetect' + + + + Replace 'DataTestMethod' with 'TestMethod' + 將 'DataTestMethod' 取代為 'TestMethod' + + Replace TestInitialize method with constructor 以建構函式取代 TestInitialize 方法 @@ -67,6 +97,11 @@ 使用 TestInitialize 方法取代建構函式 + + Use 'Assert.{0}' instead of 'StringAssert' + 使用 'Assert.{0}' 而不是 'StringAssert' + + Fix test class signature 修正測試類別簽章 @@ -92,6 +127,16 @@ 新增 '[TestMethod]' + + Use 'TestContext.CancellationToken' instead + 請改為使用 'TestContext.CancellationToken' + + + + Use 'CooperativeCancellation = true' + 使用 'CooperativeCancellation = true' + + Use '{0}' 使用 '{0}' diff --git a/src/Analyzers/MSTest.Analyzers.Package/MSTest.Analyzers.Package.csproj b/src/Analyzers/MSTest.Analyzers.Package/MSTest.Analyzers.Package.csproj index dbeb1dfb27..9a8b279bdb 100644 --- a/src/Analyzers/MSTest.Analyzers.Package/MSTest.Analyzers.Package.csproj +++ b/src/Analyzers/MSTest.Analyzers.Package/MSTest.Analyzers.Package.csproj @@ -4,7 +4,6 @@ netstandard2.0 false true - true @@ -35,7 +34,7 @@ $(DotNetRoot)dotnet.exe $(DotNetRoot)dotnet - + diff --git a/src/Analyzers/MSTest.Analyzers/AnalyzerReleases.Shipped.md b/src/Analyzers/MSTest.Analyzers/AnalyzerReleases.Shipped.md index e3dd53f75e..2aa27e3422 100644 --- a/src/Analyzers/MSTest.Analyzers/AnalyzerReleases.Shipped.md +++ b/src/Analyzers/MSTest.Analyzers/AnalyzerReleases.Shipped.md @@ -1,4 +1,33 @@ -## Release 3.8.0 +## Release 3.10.0 + +### New Rules + +Rule ID | Category | Severity | Notes +--------|----------|----------|------- +MSTEST0044 | Design | Warning | PreferTestMethodOverDataTestMethodAnalyzer, [Documentation](https://learn.microsoft.com/dotnet/core/testing/mstest-analyzers/mstest0044) +MSTEST0045 | Usage | Info | UseCooperativeCancellationForTimeoutAnalyzer, [Documentation](https://learn.microsoft.com/dotnet/core/testing/mstest-analyzers/mstest0045) +MSTEST0046 | Usage | Info | StringAssertToAssertAnalyzer, [Documentation](https://learn.microsoft.com/dotnet/core/testing/mstest-analyzers/mstest0046) +MSTEST0048 | Usage | Warning | TestContextPropertyUsageAnalyzer, [Documentation](https://learn.microsoft.com/dotnet/core/testing/mstest-analyzers/mstest0048) +MSTEST0049 | Usage | Info | FlowTestContextCancellationTokenAnalyzer, [Documentation](https://learn.microsoft.com/dotnet/core/testing/mstest-analyzers/mstest0049) +MSTEST0050 | Usage | Error | GlobalTestFixtureShouldBeValidAnalyzer, [Documentation](https://learn.microsoft.com/dotnet/core/testing/mstest-analyzers/mstest0050) + +### Changed Rules + +Rule ID | New Category | New Severity | Old Category | Old Severity | Notes +--------|--------------|--------------|--------------|--------------|------- +MSTEST0006 | Design | Warning | Design | Info | AvoidExpectedExceptionAttributeAnalyzer +MSTEST0039 | Usage | Warning | Usage | Info | UseNewerAssertThrowsAnalyzer + +## Release 3.9.0 + +### New Rules + +Rule ID | Category | Severity | Notes +--------|----------|----------|------- +MSTEST0042 | Usage | Warning | DuplicateDataRowAnalyzer, [Documentation](https://learn.microsoft.com/dotnet/core/testing/mstest-analyzers/mstest0042) +MSTEST0043 | Usage | Warning | UseRetryWithTestMethodAnalyzer, [Documentation](https://learn.microsoft.com/dotnet/core/testing/mstest-analyzers/mstest0043) + +## Release 3.8.0 ### New Rules diff --git a/src/Analyzers/MSTest.Analyzers/AnalyzerReleases.Unshipped.md b/src/Analyzers/MSTest.Analyzers/AnalyzerReleases.Unshipped.md index fbfaf78376..ffe217561a 100644 --- a/src/Analyzers/MSTest.Analyzers/AnalyzerReleases.Unshipped.md +++ b/src/Analyzers/MSTest.Analyzers/AnalyzerReleases.Unshipped.md @@ -5,5 +5,8 @@ Rule ID | Category | Severity | Notes --------|----------|----------|------- -MSTEST0042 | Usage | Warning | DuplicateDataRowAnalyzer, [Documentation](https://learn.microsoft.com/dotnet/core/testing/mstest-analyzers/mstest0042) -MSTEST0043 | Usage | Warning | UseRetryWithTestMethodAnalyzer, [Documentation](https://learn.microsoft.com/dotnet/core/testing/mstest-analyzers/mstest0043) +MSTEST0051 | Usage | Info | AssertThrowsShouldContainSingleStatementAnalyzer, [Documentation](https://learn.microsoft.com/dotnet/core/testing/mstest-analyzers/mstest0051) +MSTEST0052 | Usage | Warning | PreferDynamicDataSourceTypeAutoDetectAnalyzer, [Documentation](https://learn.microsoft.com/dotnet/core/testing/mstest-analyzers/mstest0052) +MSTEST0053 | Usage | Warning | AvoidAssertFormatParametersAnalyzer, [Documentation](https://learn.microsoft.com/dotnet/core/testing/mstest-analyzers/mstest0053) +MSTEST0054 | Usage | Info | UseCancellationTokenPropertyAnalyzer, [Documentation](https://learn.microsoft.com/dotnet/core/testing/mstest-analyzers/mstest0054) +MSTEST0055 | Usage | Warning | IgnoreStringMethodReturnValueAnalyzer, [Documentation](https://learn.microsoft.com/dotnet/core/testing/mstest-analyzers/mstest0055) diff --git a/src/Analyzers/MSTest.Analyzers/AssertThrowsShouldContainSingleStatementAnalyzer.cs b/src/Analyzers/MSTest.Analyzers/AssertThrowsShouldContainSingleStatementAnalyzer.cs new file mode 100644 index 0000000000..ef41149c65 --- /dev/null +++ b/src/Analyzers/MSTest.Analyzers/AssertThrowsShouldContainSingleStatementAnalyzer.cs @@ -0,0 +1,122 @@ +// 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; + +/// +/// MSTEST0051: Assert.Throws should contain only a single statement/expression. +/// +[DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] +public sealed class AssertThrowsShouldContainSingleStatementAnalyzer : DiagnosticAnalyzer +{ + private static readonly LocalizableResourceString Title = new(nameof(Resources.AssertThrowsShouldContainSingleStatementTitle), Resources.ResourceManager, typeof(Resources)); + private static readonly LocalizableResourceString MessageFormat = new(nameof(Resources.AssertThrowsShouldContainSingleStatementMessageFormat), Resources.ResourceManager, typeof(Resources)); + private static readonly LocalizableResourceString Description = new(nameof(Resources.AssertThrowsShouldContainSingleStatementDescription), Resources.ResourceManager, typeof(Resources)); + + internal static readonly DiagnosticDescriptor Rule = DiagnosticDescriptorHelper.Create( + DiagnosticIds.AssertThrowsShouldContainSingleStatementRuleId, + Title, + MessageFormat, + Description, + 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? assertTypeSymbol)) + { + return; + } + + context.RegisterOperationAction(context => AnalyzeInvocationOperation(context, assertTypeSymbol), OperationKind.Invocation); + }); + } + + private static void AnalyzeInvocationOperation(OperationAnalysisContext context, INamedTypeSymbol assertTypeSymbol) + { + var operation = (IInvocationOperation)context.Operation; + IMethodSymbol targetMethod = operation.TargetMethod; + + if (!SymbolEqualityComparer.Default.Equals(targetMethod.ContainingType, assertTypeSymbol) || + !targetMethod.Name.StartsWith("Throws", StringComparison.Ordinal)) + { + return; + } + + // Find the action parameter (lambda expression) - it's typically the first parameter + foreach (IArgumentOperation argument in operation.Arguments) + { + if (argument.Parameter?.Ordinal == 0) + { + AnalyzeActionArgument(context, argument.Value.WalkDownConversion(), operation); + break; + } + } + } + + private static void AnalyzeActionArgument(OperationAnalysisContext context, IOperation argumentValueOperation, IInvocationOperation invocationOperation) + { + if (argumentValueOperation is not IDelegateCreationOperation delegateCreation || + delegateCreation.Target is not IAnonymousFunctionOperation lambdaOperation || + lambdaOperation.Body is not IBlockOperation blockOperation) + { + return; + } + + // Flag if there are multiple meaningful statements + if (CountStatements(blockOperation) > 1) + { + context.ReportDiagnostic(invocationOperation.CreateDiagnostic(Rule)); + } + } + + private static int CountStatements(IBlockOperation blockOperation) + { + int statementCount = 0; + foreach (IOperation operation in blockOperation.Operations) + { + // Skip implicit return/labeled operations. + // Implicit returns don't represent user code. + // Implicit labeled operations seem to be created for lambdas only under VB. But we don't do a language check. + // TODO: Should we bail-out for any implicit operation? + if (operation is IReturnOperation or ILabeledOperation && operation.IsImplicit) + { + continue; + } + + // Skip empty statements + if (operation is IEmptyOperation) + { + continue; + } + + // If we have a nested block operation, we add the count of the statements within it. Otherwise, + // we increment by one. + statementCount += operation is IBlockOperation nestedBlock ? CountStatements(nestedBlock) : 1; + } + + return statementCount; + } +} diff --git a/src/Analyzers/MSTest.Analyzers/AssertionArgsShouldBePassedInCorrectOrderAnalyzer.cs b/src/Analyzers/MSTest.Analyzers/AssertionArgsShouldBePassedInCorrectOrderAnalyzer.cs index 1e08e3ee1f..1c10b1624f 100644 --- a/src/Analyzers/MSTest.Analyzers/AssertionArgsShouldBePassedInCorrectOrderAnalyzer.cs +++ b/src/Analyzers/MSTest.Analyzers/AssertionArgsShouldBePassedInCorrectOrderAnalyzer.cs @@ -60,7 +60,10 @@ public override void Initialize(AnalysisContext context) } private static bool IsConstant(IArgumentOperation argumentOperation) - => argumentOperation.Value.WalkDownConversion().ConstantValue.HasValue; + { + IOperation operation = argumentOperation.Value.WalkDownConversion(); + return operation.ConstantValue.HasValue || operation.Kind == OperationKind.TypeOf; + } private static void AnalyzeOperation(OperationAnalysisContext context, INamedTypeSymbol assertSymbol) { @@ -74,31 +77,50 @@ private static void AnalyzeOperation(OperationAnalysisContext context, INamedTyp return; } + // If "expected" is already constant, we shouldn't report any diagnostics and we don't care about "actual". + if (IsConstant(expectedArgument)) + { + return; + } + // If the actual value is a constant or a literal and expected is not, then the arguments are in the wrong order. // Note that we don't report if both are literals or constants, as there is no real fix for this. // If both are literals or constants, the assert will always pass or always fail. - if (IsConstant(actualArgument) && !IsConstant(expectedArgument)) + if (IsConstant(actualArgument)) { context.ReportDiagnostic(invocationOperation.CreateDiagnostic(Rule)); return; } - if (actualArgument.Value.GetReferencedMemberOrLocalOrParameter() is { } actualSymbol) + ISymbol? actualSymbol = actualArgument.Value.GetReferencedMemberOrLocalOrParameter(); + ISymbol? expectedSymbol = expectedArgument.Value.GetReferencedMemberOrLocalOrParameter(); + bool actualIsExpected = actualSymbol is not null && NameIsExpected(actualSymbol.Name); + bool expectedIsExpected = expectedSymbol is not null && NameIsExpected(expectedSymbol.Name); + + // If both arguments have names indicating it's "expected", don't report a diagnostic. + if (actualIsExpected && !expectedIsExpected) { - if (actualSymbol.Name.StartsWith("expected", StringComparison.Ordinal) - || actualSymbol.Name.StartsWith("_expected", StringComparison.Ordinal) - || actualSymbol.Name.StartsWith("Expected", StringComparison.Ordinal)) - { - context.ReportDiagnostic(invocationOperation.CreateDiagnostic(Rule)); - return; - } + context.ReportDiagnostic(invocationOperation.CreateDiagnostic(Rule)); + return; } - if (expectedArgument.Value.GetReferencedMemberOrLocalOrParameter() is { } expectedSymbol - && expectedSymbol.Name.StartsWith("actual", StringComparison.Ordinal)) + bool expectedIsActual = expectedSymbol is not null && NameIsActual(expectedSymbol.Name); + bool actualIsActual = actualSymbol is not null && NameIsActual(actualSymbol.Name); + + // If both arguments have names indicating it's "actual", don't report a diagnostic. + if (expectedIsActual && !actualIsActual) { context.ReportDiagnostic(invocationOperation.CreateDiagnostic(Rule)); + return; } + + static bool NameIsExpected(string name) + => name.StartsWith("expected", StringComparison.Ordinal) || + name.StartsWith("_expected", StringComparison.Ordinal) || + name.StartsWith("Expected", StringComparison.Ordinal); + + static bool NameIsActual(string name) + => name.StartsWith("actual", StringComparison.Ordinal); } private static (IArgumentOperation ExpectedArgument, IArgumentOperation ActualArgument)? FindExpectedAndActualArguments(IInvocationOperation invocationOperation) diff --git a/src/Analyzers/MSTest.Analyzers/AvoidAssertFormatParametersAnalyzer.cs b/src/Analyzers/MSTest.Analyzers/AvoidAssertFormatParametersAnalyzer.cs new file mode 100644 index 0000000000..3440e5e8e2 --- /dev/null +++ b/src/Analyzers/MSTest.Analyzers/AvoidAssertFormatParametersAnalyzer.cs @@ -0,0 +1,91 @@ +// 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; + +/// +/// MSTEST0053: . +/// +[DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] +public sealed class AvoidAssertFormatParametersAnalyzer : DiagnosticAnalyzer +{ + private static readonly LocalizableResourceString Title = new(nameof(Resources.AvoidAssertFormatParametersTitle), Resources.ResourceManager, typeof(Resources)); + private static readonly LocalizableResourceString MessageFormat = new(nameof(Resources.AvoidAssertFormatParametersMessageFormat), Resources.ResourceManager, typeof(Resources)); + + internal static readonly DiagnosticDescriptor Rule = DiagnosticDescriptorHelper.Create( + DiagnosticIds.AvoidAssertFormatParametersRuleId, + Title, + MessageFormat, + null, + Category.Usage, + DiagnosticSeverity.Warning, + 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.Compilation.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftVisualStudioTestToolsUnitTestingCollectionAssert, out INamedTypeSymbol? collectionAssertSymbol) && + context.Compilation.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftVisualStudioTestToolsUnitTestingStringAssert, out INamedTypeSymbol? stringAssertSymbol)) + { + context.RegisterOperationAction(context => AnalyzeOperation(context, assertSymbol, collectionAssertSymbol, stringAssertSymbol), OperationKind.Invocation); + } + }); + } + + private static void AnalyzeOperation(OperationAnalysisContext context, INamedTypeSymbol? assertSymbol, INamedTypeSymbol? collectionAssertSymbol, INamedTypeSymbol? stringAssertSymbol) + { + var invocationOperation = (IInvocationOperation)context.Operation; + + // Check if this is a call to Assert, CollectionAssert, or StringAssert + if (!IsTargetAssertType(invocationOperation.TargetMethod.ContainingType, assertSymbol, collectionAssertSymbol, stringAssertSymbol)) + { + return; + } + + // Check if this method call has the format string + params pattern + if (HasFormatStringParamsPattern(invocationOperation)) + { + context.ReportDiagnostic(invocationOperation.CreateDiagnostic(Rule, invocationOperation.TargetMethod.Name)); + } + } + + private static bool IsTargetAssertType(INamedTypeSymbol? containingType, INamedTypeSymbol? assertSymbol, INamedTypeSymbol? collectionAssertSymbol, INamedTypeSymbol? stringAssertSymbol) + => SymbolEqualityComparer.Default.Equals(containingType, assertSymbol) || + SymbolEqualityComparer.Default.Equals(containingType, collectionAssertSymbol) || + SymbolEqualityComparer.Default.Equals(containingType, stringAssertSymbol); + + private static bool HasFormatStringParamsPattern(IInvocationOperation invocationOperation) + { + ImmutableArray parameters = invocationOperation.TargetMethod.Parameters; + + // Look for the pattern: ([other params...], string message, params object[] parameters) + // The last two parameters should be string message and params object[] + return parameters.Length >= 2 && + parameters[parameters.Length - 1] is { IsParams: true, Type: IArrayTypeSymbol { ElementType.SpecialType: SpecialType.System_Object } } && + invocationOperation.Arguments.SingleOrDefault(arg => arg.Parameter?.Ordinal == parameters.Length - 1) is not IArgumentOperation + { + ArgumentKind: ArgumentKind.ParamArray, + Value: IArrayCreationOperation { Initializer.ElementValues.Length: 0 } + }; + } +} diff --git a/src/Analyzers/MSTest.Analyzers/AvoidExpectedExceptionAttributeAnalyzer.cs b/src/Analyzers/MSTest.Analyzers/AvoidExpectedExceptionAttributeAnalyzer.cs index 57d24831f3..df19f7f3e5 100644 --- a/src/Analyzers/MSTest.Analyzers/AvoidExpectedExceptionAttributeAnalyzer.cs +++ b/src/Analyzers/MSTest.Analyzers/AvoidExpectedExceptionAttributeAnalyzer.cs @@ -30,7 +30,7 @@ public sealed class AvoidExpectedExceptionAttributeAnalyzer : DiagnosticAnalyzer MessageFormat, Description, Category.Design, - DiagnosticSeverity.Info, + DiagnosticSeverity.Warning, isEnabledByDefault: true); /// diff --git a/src/Analyzers/MSTest.Analyzers/AvoidExplicitDynamicDataSourceTypeAnalyzer.cs b/src/Analyzers/MSTest.Analyzers/AvoidExplicitDynamicDataSourceTypeAnalyzer.cs new file mode 100644 index 0000000000..43aad409d3 --- /dev/null +++ b/src/Analyzers/MSTest.Analyzers/AvoidExplicitDynamicDataSourceTypeAnalyzer.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.Collections.Immutable; + +using Analyzer.Utilities.Extensions; + +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +using MSTest.Analyzers.Helpers; + +namespace MSTest.Analyzers; + +/// +/// MSTEST0052: . +/// +[DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] +public sealed class AvoidExplicitDynamicDataSourceTypeAnalyzer : DiagnosticAnalyzer +{ + private static readonly LocalizableResourceString Title = new(nameof(Resources.AvoidExplicitDynamicDataSourceTypeTitle), Resources.ResourceManager, typeof(Resources)); + private static readonly LocalizableResourceString MessageFormat = new(nameof(Resources.AvoidExplicitDynamicDataSourceTypeMessageFormat), Resources.ResourceManager, typeof(Resources)); + + internal static readonly DiagnosticDescriptor PreferAutoDetectRule = DiagnosticDescriptorHelper.Create( + DiagnosticIds.AvoidExplicitDynamicDataSourceTypeRuleId, + Title, + MessageFormat, + null, + Category.Usage, + DiagnosticSeverity.Warning, + isEnabledByDefault: true); + + /// + public override ImmutableArray SupportedDiagnostics { get; } = ImmutableArray.Create(PreferAutoDetectRule); + + /// + public override void Initialize(AnalysisContext context) + { + context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); + context.EnableConcurrentExecution(); + + context.RegisterCompilationStartAction(context => + { + if (context.Compilation.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftVisualStudioTestToolsUnitTestingDynamicDataAttribute, out INamedTypeSymbol? dynamicDataAttributeSymbol) + && context.Compilation.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftVisualStudioTestToolsUnitTestingDynamicDataSourceType, out INamedTypeSymbol? dynamicDataSourceTypeSymbol)) + { + context.RegisterSymbolAction( + context => AnalyzeSymbol(context, dynamicDataAttributeSymbol, dynamicDataSourceTypeSymbol), + SymbolKind.Method); + } + }); + } + + private static void AnalyzeSymbol(SymbolAnalysisContext context, INamedTypeSymbol dynamicDataAttributeSymbol, INamedTypeSymbol dynamicDataSourceTypeSymbol) + { + var methodSymbol = (IMethodSymbol)context.Symbol; + + foreach (AttributeData methodAttribute in methodSymbol.GetAttributes()) + { + if (SymbolEqualityComparer.Default.Equals(methodAttribute.AttributeClass, dynamicDataAttributeSymbol)) + { + AnalyzeAttribute(context, methodAttribute, dynamicDataSourceTypeSymbol); + } + } + } + + private static void AnalyzeAttribute(SymbolAnalysisContext context, AttributeData attributeData, INamedTypeSymbol dynamicDataSourceTypeSymbol) + { + if (attributeData.ApplicationSyntaxReference?.GetSyntax(context.CancellationToken) is { } syntax && + attributeData.AttributeConstructor?.Parameters.Any(p => dynamicDataSourceTypeSymbol.Equals(p.Type, SymbolEqualityComparer.Default)) == true) + { + context.ReportDiagnostic(syntax.CreateDiagnostic(PreferAutoDetectRule)); + } + } +} diff --git a/src/Analyzers/MSTest.Analyzers/AvoidUsingAssertsInAsyncVoidContextAnalyzer.cs b/src/Analyzers/MSTest.Analyzers/AvoidUsingAssertsInAsyncVoidContextAnalyzer.cs index 4a6c6e1a7a..76ecac2e37 100644 --- a/src/Analyzers/MSTest.Analyzers/AvoidUsingAssertsInAsyncVoidContextAnalyzer.cs +++ b/src/Analyzers/MSTest.Analyzers/AvoidUsingAssertsInAsyncVoidContextAnalyzer.cs @@ -46,23 +46,38 @@ public override void Initialize(AnalysisContext context) { Compilation compilation = context.Compilation; INamedTypeSymbol? assertSymbol = compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftVisualStudioTestToolsUnitTestingAssert); - if (assertSymbol is not null) + INamedTypeSymbol? stringAssertSymbol = compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftVisualStudioTestToolsUnitTestingStringAssert); + INamedTypeSymbol? collectionAssertSymbol = compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftVisualStudioTestToolsUnitTestingCollectionAssert); + + if (assertSymbol is not null || stringAssertSymbol is not null || collectionAssertSymbol is not null) { - context.RegisterOperationAction(context => AnalyzeOperation(context, assertSymbol), OperationKind.Invocation); + context.RegisterOperationAction(context => AnalyzeOperation(context, assertSymbol, stringAssertSymbol, collectionAssertSymbol), OperationKind.Invocation); } }); } - private static void AnalyzeOperation(OperationAnalysisContext context, INamedTypeSymbol assertSymbol) + private static void AnalyzeOperation( + OperationAnalysisContext context, + INamedTypeSymbol? assertSymbol, + INamedTypeSymbol? stringAssertSymbol, + INamedTypeSymbol? collectionAssertSymbol) { var operation = (IInvocationOperation)context.Operation; - if (!IsAsyncVoidContext(operation, context.ContainingSymbol) || - !assertSymbol.Equals(operation.TargetMethod.ContainingType, SymbolEqualityComparer.Default)) + if (!IsAsyncVoidContext(operation, context.ContainingSymbol)) { return; } - context.ReportDiagnostic(operation.CreateDiagnostic(Rule)); + INamedTypeSymbol targetType = operation.TargetMethod.ContainingType; + bool isAssertType = + targetType.Equals(assertSymbol, SymbolEqualityComparer.Default) || + targetType.Equals(stringAssertSymbol, SymbolEqualityComparer.Default) || + targetType.Equals(collectionAssertSymbol, SymbolEqualityComparer.Default); + + if (isAssertType) + { + context.ReportDiagnostic(operation.CreateDiagnostic(Rule)); + } } private static bool IsAsyncVoidContext(IInvocationOperation invocationOperation, ISymbol containingSymbol) diff --git a/src/Analyzers/MSTest.Analyzers/DataRowShouldBeValidAnalyzer.cs b/src/Analyzers/MSTest.Analyzers/DataRowShouldBeValidAnalyzer.cs index 1742bf29c9..44d17d7c2d 100644 --- a/src/Analyzers/MSTest.Analyzers/DataRowShouldBeValidAnalyzer.cs +++ b/src/Analyzers/MSTest.Analyzers/DataRowShouldBeValidAnalyzer.cs @@ -91,7 +91,7 @@ private static void AnalyzeSymbol( var methodSymbol = (IMethodSymbol)context.Symbol; bool isTestMethod = false; - List dataRowAttributes = new(); + List dataRowAttributes = []; foreach (AttributeData methodAttribute in methodSymbol.GetAttributes()) { // Current method should be a test method or should inherit from the TestMethod attribute. @@ -184,7 +184,7 @@ private static void AnalyzeAttribute(SymbolAnalysisContext context, AttributeDat AnalyzeGenericMethod(context, dataRowSyntax, methodSymbol, constructorArguments); // Check constructor argument types match method parameter types. - List<(int ConstructorArgumentIndex, int MethodParameterIndex)> typeMismatchIndices = new(); + List<(string ParameterName, string ExpectedType, string ActualType)> typeMismatches = []; for (int currentArgumentIndex = 0; currentArgumentIndex < constructorArguments.Length; currentArgumentIndex++) { // Null is considered as default for non-nullable types. @@ -205,16 +205,34 @@ private static void AnalyzeAttribute(SymbolAnalysisContext context, AttributeDat if (argumentType is not null && !argumentType.IsAssignableTo(paramType, context.Compilation)) { - typeMismatchIndices.Add((currentArgumentIndex, Math.Min(currentArgumentIndex, methodSymbol.Parameters.Length - 1))); + int parameterIndex = Math.Min(currentArgumentIndex, methodSymbol.Parameters.Length - 1); + string parameterName = methodSymbol.Parameters[parameterIndex].Name; + string expectedType = paramType.ToDisplayString(); + string actualType = argumentType.ToDisplayString(); + typeMismatches.Add((parameterName, expectedType, actualType)); } } // Report diagnostics if there's any type mismatch. - if (typeMismatchIndices.Count > 0) + if (typeMismatches.Count > 0) { + // Format all mismatches into a single message + string mismatchMessage; + if (typeMismatches.Count == 1) + { + (string parameterName, string expectedType, string actualType) = typeMismatches[0]; + mismatchMessage = string.Format(CultureInfo.InvariantCulture, Resources.DataRowShouldBeValidMessageFormat_ParameterMismatch, parameterName, expectedType, actualType); + } + else + { + IEnumerable mismatchDescriptions = typeMismatches.Select(m => + string.Format(CultureInfo.InvariantCulture, Resources.DataRowShouldBeValidMessageFormat_ParameterMismatch, m.ParameterName, m.ExpectedType, m.ActualType)); + mismatchMessage = string.Join("; ", mismatchDescriptions); + } + context.ReportDiagnostic(dataRowSyntax.CreateDiagnostic( ArgumentTypeMismatchRule, - string.Join(", ", typeMismatchIndices))); + mismatchMessage)); } } diff --git a/src/Analyzers/MSTest.Analyzers/DoNotUseShadowingAnalyzer.cs b/src/Analyzers/MSTest.Analyzers/DoNotUseShadowingAnalyzer.cs index e326e9cff5..99f2fca090 100644 --- a/src/Analyzers/MSTest.Analyzers/DoNotUseShadowingAnalyzer.cs +++ b/src/Analyzers/MSTest.Analyzers/DoNotUseShadowingAnalyzer.cs @@ -64,7 +64,7 @@ private static void AnalyzeSymbol(SymbolAnalysisContext context, INamedTypeSymbo Dictionary> membersByName = GetBaseMembers(namedTypeSymbol); foreach (ISymbol member in namedTypeSymbol.GetMembers()) { - foreach (ISymbol baseMember in membersByName.GetValueOrDefault(member.Name, new List())) + foreach (ISymbol baseMember in membersByName.GetValueOrDefault(member.Name, [])) { // Check if the member is shadowing a base class member if (IsMemberShadowing(member, baseMember)) @@ -77,7 +77,7 @@ private static void AnalyzeSymbol(SymbolAnalysisContext context, INamedTypeSymbo private static Dictionary> GetBaseMembers(INamedTypeSymbol namedTypeSymbol) { - Dictionary> membersByName = new(); + Dictionary> membersByName = []; INamedTypeSymbol? currentType = namedTypeSymbol.BaseType; while (currentType is not null) { @@ -91,7 +91,7 @@ private static Dictionary> GetBaseMembers(INamedTypeSymbol if (!membersByName.TryGetValue(member.Name, out List? members)) { - members = new List(); + members = []; membersByName[member.Name] = members; } diff --git a/src/Analyzers/MSTest.Analyzers/DuplicateDataRowAnalyzer.cs b/src/Analyzers/MSTest.Analyzers/DuplicateDataRowAnalyzer.cs index b4c2596fe9..6d560c76c1 100644 --- a/src/Analyzers/MSTest.Analyzers/DuplicateDataRowAnalyzer.cs +++ b/src/Analyzers/MSTest.Analyzers/DuplicateDataRowAnalyzer.cs @@ -126,6 +126,22 @@ private static bool AreTypedConstantEquals(TypedConstant typedConstant1, TypedCo return TypedConstantArrayComparer.Instance.Equals(typedConstant1.Values, typedConstant2.Values); } + if (typedConstant1.Kind == TypedConstantKind.Primitive) + { + // object.Equals(float.NegativeZero, 0.0f) will return true. + // But we don't want to consider it as "equal" as the test case can yield different results. + // Behavior difference between zero and negative zero can be observed via BitConverter or ToString. + if (typedConstant1.Value is float float1 && typedConstant2.Value is float float2) + { + // BitConverter.SingleToInt32Bits isn't available on netstandard2.0, so we use BitConverter.GetBytes instead. + return BitConverter.GetBytes(float1).SequenceEqual(BitConverter.GetBytes(float2)); + } + else if (typedConstant1.Value is double double1 && typedConstant2.Value is double double2) + { + return BitConverter.DoubleToInt64Bits(double1) == BitConverter.DoubleToInt64Bits(double2); + } + } + // At this point, the type is matching and the kind is matching and is not array. return object.Equals(typedConstant1.Value, typedConstant2.Value); } diff --git a/src/Analyzers/MSTest.Analyzers/DynamicDataShouldBeValidAnalyzer.cs b/src/Analyzers/MSTest.Analyzers/DynamicDataShouldBeValidAnalyzer.cs index d5af2d294f..3c3d1a3e1e 100644 --- a/src/Analyzers/MSTest.Analyzers/DynamicDataShouldBeValidAnalyzer.cs +++ b/src/Analyzers/MSTest.Analyzers/DynamicDataShouldBeValidAnalyzer.cs @@ -21,6 +21,7 @@ public sealed class DynamicDataShouldBeValidAnalyzer : DiagnosticAnalyzer private const int DynamicDataSourceTypeProperty = 0; private const int DynamicDataSourceTypeMethod = 1; private const int DynamicDataSourceTypeAutoDetect = 2; + private const int DynamicDataSourceTypeField = 3; private static readonly LocalizableResourceString Title = new(nameof(Resources.DynamicDataShouldBeValidTitle), Resources.ResourceManager, typeof(Resources)); private static readonly LocalizableResourceString Description = new(nameof(Resources.DynamicDataShouldBeValidDescription), Resources.ResourceManager, typeof(Resources)); @@ -47,8 +48,11 @@ public sealed class DynamicDataShouldBeValidAnalyzer : DiagnosticAnalyzer internal static readonly DiagnosticDescriptor SourceTypeMethodRule = NotTestMethodRule .WithMessage(new(nameof(Resources.DynamicDataShouldBeValidMessageFormat_SourceTypeMethod), Resources.ResourceManager, typeof(Resources))); + internal static readonly DiagnosticDescriptor SourceTypeFieldRule = NotTestMethodRule + .WithMessage(new(nameof(Resources.DynamicDataShouldBeValidMessageFormat_SourceTypeField), Resources.ResourceManager, typeof(Resources))); + internal static readonly DiagnosticDescriptor SourceTypeNotPropertyOrMethodRule = NotTestMethodRule - .WithMessage(new(nameof(Resources.DynamicDataShouldBeValidMessageFormat_SourceTypeNotPropertyOrMethod), Resources.ResourceManager, typeof(Resources))); + .WithMessage(new(nameof(Resources.DynamicDataShouldBeValidMessageFormat_SourceTypeNotPropertyMethodOrField), Resources.ResourceManager, typeof(Resources))); internal static readonly DiagnosticDescriptor MemberMethodRule = NotTestMethodRule .WithMessage(new(nameof(Resources.DynamicDataShouldBeValidMessageFormat_MemberMethod), Resources.ResourceManager, typeof(Resources))); @@ -184,6 +188,7 @@ private static void AnalyzeDataSource(SymbolAnalysisContext context, AttributeDa { string? memberName = null; int dataSourceType = DynamicDataSourceTypeAutoDetect; + int argumentsCount = 0; INamedTypeSymbol declaringType = methodSymbol.ContainingType; foreach (TypedConstant argument in attributeData.ConstructorArguments) { @@ -202,10 +207,15 @@ private static void AnalyzeDataSource(SymbolAnalysisContext context, AttributeDa { dataSourceType = dataType; } - else if (argument.Value is INamedTypeSymbol type) + else if (argument.Kind != TypedConstantKind.Array && + argument.Value is INamedTypeSymbol type) { declaringType = type; } + else if (argument.Kind == TypedConstantKind.Array) + { + argumentsCount = argument.Values.Length; + } } // If the member name is not available, bail out. @@ -250,6 +260,15 @@ private static void AnalyzeDataSource(SymbolAnalysisContext context, AttributeDa return; } + break; + case SymbolKind.Field: + // If the member is a field and the data source type is not set to field or auto detect, report a diagnostic. + if (dataSourceType is not (DynamicDataSourceTypeField or DynamicDataSourceTypeAutoDetect)) + { + context.ReportDiagnostic(attributeSyntax.CreateDiagnostic(SourceTypeFieldRule, declaringType.Name, memberName)); + return; + } + break; default: context.ReportDiagnostic(attributeSyntax.CreateDiagnostic(SourceTypeNotPropertyOrMethodRule, declaringType.Name, memberName)); @@ -264,7 +283,7 @@ private static void AnalyzeDataSource(SymbolAnalysisContext context, AttributeDa if (member.Kind == SymbolKind.Method && member is IMethodSymbol method - && (method.IsGenericMethod || method.Parameters.Length != 0)) + && (method.IsGenericMethod || method.Parameters.Length != argumentsCount)) { context.ReportDiagnostic(attributeSyntax.CreateDiagnostic(DataMemberSignatureRule, declaringType.Name, memberName)); return; @@ -272,7 +291,7 @@ private static void AnalyzeDataSource(SymbolAnalysisContext context, AttributeDa // Validate member return type. ITypeSymbol? memberTypeSymbol = member.GetMemberType(); - if (memberTypeSymbol is IArrayTypeSymbol arrayType) + if (memberTypeSymbol is IArrayTypeSymbol) { return; } diff --git a/src/Analyzers/MSTest.Analyzers/FlowTestContextCancellationTokenAnalyzer.cs b/src/Analyzers/MSTest.Analyzers/FlowTestContextCancellationTokenAnalyzer.cs new file mode 100644 index 0000000000..388b893439 --- /dev/null +++ b/src/Analyzers/MSTest.Analyzers/FlowTestContextCancellationTokenAnalyzer.cs @@ -0,0 +1,259 @@ +// 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; + +/// +/// MSTEST0049: . +/// +[DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] +public sealed class FlowTestContextCancellationTokenAnalyzer : DiagnosticAnalyzer +{ + private static readonly LocalizableResourceString Title = new(nameof(Resources.FlowTestContextCancellationTokenTitle), Resources.ResourceManager, typeof(Resources)); + private static readonly LocalizableResourceString Description = new(nameof(Resources.FlowTestContextCancellationTokenDescription), Resources.ResourceManager, typeof(Resources)); + private static readonly LocalizableResourceString MessageFormat = new(nameof(Resources.FlowTestContextCancellationTokenMessageFormat), Resources.ResourceManager, typeof(Resources)); + + internal const string TestContextMemberNamePropertyKey = nameof(TestContextMemberNamePropertyKey); + internal const string CancellationTokenParameterNamePropertyKey = nameof(CancellationTokenParameterNamePropertyKey); + + internal static readonly DiagnosticDescriptor FlowTestContextCancellationTokenRule = DiagnosticDescriptorHelper.Create( + DiagnosticIds.FlowTestContextCancellationTokenRuleId, + Title, + MessageFormat, + Description, + Category.Usage, + DiagnosticSeverity.Info, + isEnabledByDefault: true); + + /// + public override ImmutableArray SupportedDiagnostics { get; } + = ImmutableArray.Create(FlowTestContextCancellationTokenRule); + + /// + public override void Initialize(AnalysisContext context) + { + context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); + context.EnableConcurrentExecution(); + + context.RegisterCompilationStartAction(context => + { + // Get the required symbols + if (!context.Compilation.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemThreadingCancellationToken, out INamedTypeSymbol? cancellationTokenSymbol) || + !context.Compilation.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftVisualStudioTestToolsUnitTestingTestContext, out INamedTypeSymbol? testContextSymbol) || + !context.Compilation.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftVisualStudioTestToolsUnitTestingClassCleanupAttribute, out INamedTypeSymbol? classCleanupAttributeSymbol) || + !context.Compilation.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftVisualStudioTestToolsUnitTestingAssemblyCleanupAttribute, out INamedTypeSymbol? assemblyCleanupAttributeSymbol) || + !context.Compilation.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftVisualStudioTestToolsUnitTestingTestMethodAttribute, out INamedTypeSymbol? testMethodAttributeSymbol)) + { + return; + } + + context.RegisterOperationAction( + context => AnalyzeInvocation(context, cancellationTokenSymbol, testContextSymbol, classCleanupAttributeSymbol, assemblyCleanupAttributeSymbol, testMethodAttributeSymbol), + OperationKind.Invocation); + }); + } + + private static void AnalyzeInvocation( + OperationAnalysisContext context, + INamedTypeSymbol cancellationTokenSymbol, + INamedTypeSymbol testContextSymbol, + INamedTypeSymbol classCleanupAttributeSymbol, + INamedTypeSymbol assemblyCleanupAttributeSymbol, + INamedTypeSymbol testMethodAttributeSymbol) + { + var invocationOperation = (IInvocationOperation)context.Operation; + IMethodSymbol method = invocationOperation.TargetMethod; + + // Check if we're in a context where a TestContext is already available or could be made available. + if (!HasOrCouldHaveTestContextInScope(context.ContainingSymbol, testContextSymbol, classCleanupAttributeSymbol, assemblyCleanupAttributeSymbol, testMethodAttributeSymbol, out string? testContextMemberNameInScope, out TestContextState? testContextState)) + { + return; + } + + IParameterSymbol? cancellationTokenParameter = method.Parameters.LastOrDefault(p => SymbolEqualityComparer.Default.Equals(p.Type, cancellationTokenSymbol)); + bool parameterHasDefaultValue = cancellationTokenParameter is not null && cancellationTokenParameter.HasExplicitDefaultValue; + if (cancellationTokenParameter is not null && !parameterHasDefaultValue) + { + // The called method has a required CancellationToken parameter. + // No need to report diagnostic, even if user is explicitly passing CancellationToken.None or default(CancellationToken). + // We consider it "intentional" if the user is passing it explicitly. + return; + } + + if (parameterHasDefaultValue && + invocationOperation.Arguments.FirstOrDefault(arg => SymbolEqualityComparer.Default.Equals(arg.Parameter, cancellationTokenParameter))?.ArgumentKind != ArgumentKind.Explicit) + { + // The called method has an optional CancellationToken parameter, but it was not explicitly provided. So we report a diagnostic. + // We also pass non-null cancellationTokenParameterName if the codefix should use named argument. + string? cancellationTokenParameterName = null; + int indexOfParameterCorrespondingToLastExplicitArgument = invocationOperation.Arguments.LastOrDefault(arg => arg.ArgumentKind == ArgumentKind.Explicit)?.Parameter?.Ordinal ?? -1; + if (cancellationTokenParameter!.Ordinal != indexOfParameterCorrespondingToLastExplicitArgument + 1) + { + cancellationTokenParameterName = cancellationTokenParameter.Name; + } + + context.ReportDiagnostic(invocationOperation.Syntax.CreateDiagnostic(FlowTestContextCancellationTokenRule, properties: GetPropertiesBag(testContextMemberNameInScope, testContextState, cancellationTokenParameterName))); + return; + } + + // At this point, we want to only continue analysis if and only if the called method didn't have a CancellationToken parameter. + // In this case, we look for other overloads that might accept a CancellationToken. + if (cancellationTokenParameter is null && + GetCancellationTokenParameterOfOverloadWithCancellationToken(method, cancellationTokenSymbol) is { } cancellationTokenParameterFromDifferentOverload) + { + string? cancellationTokenParameterName = null; + int indexOfParameterCorrespondingToLastExplicitArgument = invocationOperation.Arguments.LastOrDefault(arg => arg.ArgumentKind == ArgumentKind.Explicit)?.Parameter?.Ordinal ?? -1; + + if (cancellationTokenParameterFromDifferentOverload.Ordinal != indexOfParameterCorrespondingToLastExplicitArgument + 1) + { + cancellationTokenParameterName = cancellationTokenParameterFromDifferentOverload.Name; + } + + context.ReportDiagnostic(invocationOperation.Syntax.CreateDiagnostic(FlowTestContextCancellationTokenRule, properties: GetPropertiesBag(testContextMemberNameInScope, testContextState, cancellationTokenParameterName))); + } + + static ImmutableDictionary GetPropertiesBag(string? testContextMemberNameInScope, TestContextState? testContextState, string? cancellationTokenParameterName) + { + ImmutableDictionary properties = ImmutableDictionary.Empty; + properties = testContextMemberNameInScope is not null + ? properties.Add(TestContextMemberNamePropertyKey, testContextMemberNameInScope) + : properties.Add(nameof(TestContextState), testContextState.ToString()); + + if (cancellationTokenParameterName is not null) + { + properties = properties.Add(CancellationTokenParameterNamePropertyKey, cancellationTokenParameterName); + } + + return properties; + } + } + + private static IParameterSymbol? GetCancellationTokenParameterOfOverloadWithCancellationToken(IMethodSymbol method, INamedTypeSymbol cancellationTokenSymbol) + { + // Look for overloads of the same method that accept CancellationToken + INamedTypeSymbol containingType = method.ContainingType; + + foreach (ISymbol member in containingType.GetMembers(method.Name)) + { + if (member is IMethodSymbol candidateMethod && + candidateMethod.MethodKind == method.MethodKind && + candidateMethod.IsStatic == method.IsStatic) + { + // Check if this method has the same parameters plus a CancellationToken + if (GetCancellationTokenParameterIfCandidateIsValid(method, candidateMethod, cancellationTokenSymbol) is { } parameter) + { + return parameter; + } + } + } + + return null; + } + + private static bool HasOrCouldHaveTestContextInScope( + ISymbol containingSymbol, + INamedTypeSymbol testContextSymbol, + INamedTypeSymbol classCleanupAttributeSymbol, + INamedTypeSymbol assemblyCleanupAttributeSymbol, + INamedTypeSymbol testMethodAttributeSymbol, + out string? testContextMemberNameInScope, + [NotNullWhen(true)] out TestContextState? testContextState) + { + testContextMemberNameInScope = null; + testContextState = null; + + if (containingSymbol is not IMethodSymbol method) + { + return false; + } + + // We have a TestContext in scope (as a parameter) + if (method.Parameters.FirstOrDefault(p => testContextSymbol.Equals(p.Type, SymbolEqualityComparer.Default)) is { } testContextParameter) + { + testContextMemberNameInScope = testContextParameter.Name; + testContextState = TestContextState.InScope; + return true; + } + + // We have a TestContext in scope (as a field or property) + if (!method.IsStatic && + method.ContainingType.GetMembers().FirstOrDefault( + m => !m.IsStatic && m.Kind is SymbolKind.Field or SymbolKind.Property && testContextSymbol.Equals(m.GetMemberType(), SymbolEqualityComparer.Default)) is { } testContextMember) + { + testContextMember = (testContextMember as IFieldSymbol)?.AssociatedSymbol ?? testContextMember; + // Workaround https://github.com/dotnet/roslyn/issues/70208 + // https://github.com/dotnet/roslyn/blob/f25ae8e02a91169f45060951a168b233ad588ed3/src/Compilers/CSharp/Portable/Symbols/Synthesized/GeneratedNameKind.cs#L47 + testContextMemberNameInScope = testContextMember.Name.StartsWith('<') && testContextMember.Name.EndsWith(">P", StringComparison.Ordinal) + ? testContextMember.Name.Substring(1, testContextMember.Name.Length - 3) + : testContextMember.Name; + testContextState = TestContextState.InScope; + return true; + } + + // If we have AssemblyCleanup or ClassCleanup with no parameters, then we *could* have a TestContext in scope. + // Note that assembly/class cleanup method can optionally have a TestContext parameter, but it is not required. + // Also, for test methods (parameterized or not), we *could* have a TestContext in scope by adding a property to the test class (or injecting it via constructor). + ImmutableArray attributes = method.GetAttributes(); + foreach (AttributeData attribute in attributes) + { + if (method.Parameters.IsEmpty && + (classCleanupAttributeSymbol.Equals(attribute.AttributeClass, SymbolEqualityComparer.Default) || + assemblyCleanupAttributeSymbol.Equals(attribute.AttributeClass, SymbolEqualityComparer.Default))) + { + testContextState = TestContextState.CouldBeInScopeAsParameter; + return true; + } + + if (attribute.AttributeClass?.Inherits(testMethodAttributeSymbol) == true) + { + testContextState = TestContextState.CouldBeInScopeAsProperty; + return true; + } + } + + return false; + } + + private static IParameterSymbol? GetCancellationTokenParameterIfCandidateIsValid(IMethodSymbol originalMethod, IMethodSymbol candidateMethod, INamedTypeSymbol cancellationTokenSymbol) + { + // Check if the candidate method has all the same parameters as the original method plus a CancellationToken + ImmutableArray originalParams = originalMethod.Parameters; + ImmutableArray candidateParams = candidateMethod.Parameters; + + // The candidate should have one more parameter (the CancellationToken) + if (candidateParams.Length != originalParams.Length + 1) + { + return null; + } + + // Check if all original parameters match the first N parameters of the candidate + for (int i = 0; i < originalParams.Length; i++) + { + if (!SymbolEqualityComparer.Default.Equals(originalParams[i].Type, candidateParams[i].Type)) + { + return null; + } + } + + // Check if the last parameter is CancellationToken + IParameterSymbol lastParam = candidateParams[candidateParams.Length - 1]; + return SymbolEqualityComparer.Default.Equals(lastParam.Type, cancellationTokenSymbol) ? lastParam : null; + } + + internal enum TestContextState + { + InScope, + CouldBeInScopeAsParameter, + CouldBeInScopeAsProperty, + } +} diff --git a/src/Analyzers/MSTest.Analyzers/GlobalTestFixtureShouldBeValidAnalyzer.cs b/src/Analyzers/MSTest.Analyzers/GlobalTestFixtureShouldBeValidAnalyzer.cs new file mode 100644 index 0000000000..f7b4a5c8a8 --- /dev/null +++ b/src/Analyzers/MSTest.Analyzers/GlobalTestFixtureShouldBeValidAnalyzer.cs @@ -0,0 +1,74 @@ +// 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; + +/// +/// MSTEST0050: . +/// +[DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] +public sealed class GlobalTestFixtureShouldBeValidAnalyzer : DiagnosticAnalyzer +{ + internal static readonly DiagnosticDescriptor Rule = DiagnosticDescriptorHelper.Create( + DiagnosticIds.GlobalTestFixtureShouldBeValidRuleId, + new LocalizableResourceString(nameof(Resources.GlobalTestFixtureShouldBeValidTitle), Resources.ResourceManager, typeof(Resources)), + new LocalizableResourceString(nameof(Resources.GlobalTestFixtureShouldBeValidMessageFormat), Resources.ResourceManager, typeof(Resources)), + new LocalizableResourceString(nameof(Resources.GlobalTestFixtureShouldBeValidDescription), Resources.ResourceManager, typeof(Resources)), + Category.Usage, + DiagnosticSeverity.Error, + 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.MicrosoftVisualStudioTestToolsUnitTestingTestContext, out INamedTypeSymbol? testContextSymbol) && + context.Compilation.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftVisualStudioTestToolsUnitTestingTestClassAttribute, out INamedTypeSymbol? testClassAttributeSymbol) && + context.Compilation.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftVisualStudioTestToolsUnitTestingGlobalTestInitializeAttribute, out INamedTypeSymbol? globalTestInitializeAttributeSymbol) && + context.Compilation.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftVisualStudioTestToolsUnitTestingGlobalTestCleanupAttribute, out INamedTypeSymbol? globalTestCleanupAttributeSymbol)) + { + INamedTypeSymbol? taskSymbol = context.Compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemThreadingTasksTask); + INamedTypeSymbol? valueTaskSymbol = context.Compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemThreadingTasksValueTask); + + context.RegisterSymbolAction( + context => AnalyzeSymbol(context, globalTestInitializeAttributeSymbol, globalTestCleanupAttributeSymbol, taskSymbol, valueTaskSymbol, testContextSymbol, testClassAttributeSymbol), + SymbolKind.Method); + } + }); + } + + private static void AnalyzeSymbol( + SymbolAnalysisContext context, + INamedTypeSymbol globalTestInitializeAttributeSymbol, + INamedTypeSymbol globalTestCleanupAttributeSymbol, + INamedTypeSymbol? taskSymbol, + INamedTypeSymbol? valueTaskSymbol, + INamedTypeSymbol testContextSymbol, + INamedTypeSymbol testClassAttributeSymbol) + { + var methodSymbol = (IMethodSymbol)context.Symbol; + + if ((methodSymbol.HasAttribute(globalTestInitializeAttributeSymbol) || methodSymbol.HasAttribute(globalTestCleanupAttributeSymbol)) && + !methodSymbol.HasValidFixtureMethodSignature(taskSymbol, valueTaskSymbol, canDiscoverInternals: false, shouldBeStatic: true, + allowGenericType: false, FixtureParameterMode.MustHaveTestContext, testContextSymbol, testClassAttributeSymbol, fixtureAllowInheritedTestClass: false, out _)) + { + context.ReportDiagnostic(methodSymbol.CreateDiagnostic(Rule, methodSymbol.Name)); + } + } +} diff --git a/src/Analyzers/MSTest.Analyzers/Helpers/DiagnosticDescriptorHelper.cs b/src/Analyzers/MSTest.Analyzers/Helpers/DiagnosticDescriptorHelper.cs index c719ef7aae..9e09a07c97 100644 --- a/src/Analyzers/MSTest.Analyzers/Helpers/DiagnosticDescriptorHelper.cs +++ b/src/Analyzers/MSTest.Analyzers/Helpers/DiagnosticDescriptorHelper.cs @@ -31,7 +31,7 @@ public static DiagnosticDescriptor Create( public static DiagnosticDescriptor WithMessage(this DiagnosticDescriptor diagnosticDescriptor, LocalizableResourceString messageFormat) => new(diagnosticDescriptor.Id, diagnosticDescriptor.Title, messageFormat, diagnosticDescriptor.Category, diagnosticDescriptor.DefaultSeverity, - diagnosticDescriptor.IsEnabledByDefault, diagnosticDescriptor.Description, diagnosticDescriptor.HelpLinkUri, diagnosticDescriptor.CustomTags.ToArray()); + diagnosticDescriptor.IsEnabledByDefault, diagnosticDescriptor.Description, diagnosticDescriptor.HelpLinkUri, [.. diagnosticDescriptor.CustomTags]); private static string[] CreateCustomTags(bool isReportedAtCompilationEnd, bool escalateToErrorInRecommended, bool disableInAllMode, string[] customTags) { diff --git a/src/Analyzers/MSTest.Analyzers/Helpers/DiagnosticIds.cs b/src/Analyzers/MSTest.Analyzers/Helpers/DiagnosticIds.cs index 66be4e9c99..14b7dc9b8e 100644 --- a/src/Analyzers/MSTest.Analyzers/Helpers/DiagnosticIds.cs +++ b/src/Analyzers/MSTest.Analyzers/Helpers/DiagnosticIds.cs @@ -48,4 +48,16 @@ internal static class DiagnosticIds public const string UseConditionBaseWithTestClassRuleId = "MSTEST0041"; public const string DuplicateDataRowRuleId = "MSTEST0042"; public const string UseRetryWithTestMethodRuleId = "MSTEST0043"; + public const string PreferTestMethodOverDataTestMethodRuleId = "MSTEST0044"; + public const string UseCooperativeCancellationForTimeoutRuleId = "MSTEST0045"; + public const string StringAssertToAssertRuleId = "MSTEST0046"; + public const string UnusedParameterSuppressorRuleId = "MSTEST0047"; + public const string TestContextPropertyUsageRuleId = "MSTEST0048"; + public const string FlowTestContextCancellationTokenRuleId = "MSTEST0049"; + public const string GlobalTestFixtureShouldBeValidRuleId = "MSTEST0050"; + public const string AssertThrowsShouldContainSingleStatementRuleId = "MSTEST0051"; + public const string AvoidExplicitDynamicDataSourceTypeRuleId = "MSTEST0052"; + public const string AvoidAssertFormatParametersRuleId = "MSTEST0053"; + public const string UseCancellationTokenPropertyRuleId = "MSTEST0054"; + public const string IgnoreStringMethodReturnValueRuleId = "MSTEST0055"; } diff --git a/src/Analyzers/MSTest.Analyzers/Helpers/WellKnownTypeNames.cs b/src/Analyzers/MSTest.Analyzers/Helpers/WellKnownTypeNames.cs index bb71ecd3a5..9afefad540 100644 --- a/src/Analyzers/MSTest.Analyzers/Helpers/WellKnownTypeNames.cs +++ b/src/Analyzers/MSTest.Analyzers/Helpers/WellKnownTypeNames.cs @@ -18,6 +18,7 @@ internal static class WellKnownTypeNames 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"; + public const string MicrosoftVisualStudioTestToolsUnitTestingDataTestMethodAttribute = "Microsoft.VisualStudio.TestTools.UnitTesting.DataTestMethodAttribute"; public const string MicrosoftVisualStudioTestToolsUnitTestingDeploymentItemAttribute = "Microsoft.VisualStudio.TestTools.UnitTesting.DeploymentItemAttribute"; public const string MicrosoftVisualStudioTestToolsUnitTestingDescriptionAttribute = "Microsoft.VisualStudio.TestTools.UnitTesting.DescriptionAttribute"; public const string MicrosoftVisualStudioTestToolsUnitTestingDiscoverInternalsAttribute = "Microsoft.VisualStudio.TestTools.UnitTesting.DiscoverInternalsAttribute"; @@ -26,6 +27,8 @@ internal static class WellKnownTypeNames public const string MicrosoftVisualStudioTestToolsUnitTestingDynamicDataSourceType = "Microsoft.VisualStudio.TestTools.UnitTesting.DynamicDataSourceType"; public const string MicrosoftVisualStudioTestToolsUnitTestingExpectedExceptionAttribute = "Microsoft.VisualStudio.TestTools.UnitTesting.ExpectedExceptionAttribute"; public const string MicrosoftVisualStudioTestToolsUnitTestingExpectedExceptionBaseAttribute = "Microsoft.VisualStudio.TestTools.UnitTesting.ExpectedExceptionBaseAttribute"; + public const string MicrosoftVisualStudioTestToolsUnitTestingGlobalTestCleanupAttribute = "Microsoft.VisualStudio.TestTools.UnitTesting.GlobalTestCleanupAttribute"; + public const string MicrosoftVisualStudioTestToolsUnitTestingGlobalTestInitializeAttribute = "Microsoft.VisualStudio.TestTools.UnitTesting.GlobalTestInitializeAttribute"; public const string MicrosoftVisualStudioTestToolsUnitTestingIgnoreAttribute = "Microsoft.VisualStudio.TestTools.UnitTesting.IgnoreAttribute"; public const string MicrosoftVisualStudioTestToolsUnitTestingInheritanceBehavior = "Microsoft.VisualStudio.TestTools.UnitTesting.InheritanceBehavior"; public const string MicrosoftVisualStudioTestToolsUnitTestingOwnerAttribute = "Microsoft.VisualStudio.TestTools.UnitTesting.OwnerAttribute"; @@ -39,6 +42,7 @@ internal static class WellKnownTypeNames public const string MicrosoftVisualStudioTestToolsUnitTestingTestInitializeAttribute = "Microsoft.VisualStudio.TestTools.UnitTesting.TestInitializeAttribute"; public const string MicrosoftVisualStudioTestToolsUnitTestingTestMethodAttribute = "Microsoft.VisualStudio.TestTools.UnitTesting.TestMethodAttribute"; public const string MicrosoftVisualStudioTestToolsUnitTestingTestPropertyAttribute = "Microsoft.VisualStudio.TestTools.UnitTesting.TestPropertyAttribute"; + public const string MicrosoftVisualStudioTestToolsUnitTestingTimeoutAttribute = "Microsoft.VisualStudio.TestTools.UnitTesting.TimeoutAttribute"; public const string MicrosoftVisualStudioTestToolsUnitTestingWorkItemAttribute = "Microsoft.VisualStudio.TestTools.UnitTesting.WorkItemAttribute"; public const string System = "System"; @@ -48,6 +52,9 @@ internal static class WellKnownTypeNames public const string SystemIAsyncDisposable = "System.IAsyncDisposable"; public const string SystemIDisposable = "System.IDisposable"; public const string SystemReflectionMethodInfo = "System.Reflection.MethodInfo"; + public const string SystemString = "System.String"; + public const string SystemThreadingCancellationToken = "System.Threading.CancellationToken"; + public const string SystemThreadingCancellationTokenSource = "System.Threading.CancellationTokenSource"; 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"; diff --git a/src/Analyzers/MSTest.Analyzers/IgnoreStringMethodReturnValueAnalyzer.cs b/src/Analyzers/MSTest.Analyzers/IgnoreStringMethodReturnValueAnalyzer.cs new file mode 100644 index 0000000000..7e3d0d49c7 --- /dev/null +++ b/src/Analyzers/MSTest.Analyzers/IgnoreStringMethodReturnValueAnalyzer.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.Collections.Immutable; + +using Analyzer.Utilities.Extensions; + +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Operations; + +using MSTest.Analyzers.Helpers; + +namespace MSTest.Analyzers; + +/// +/// MSTEST0055: . +/// +[DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] +public sealed class IgnoreStringMethodReturnValueAnalyzer : DiagnosticAnalyzer +{ + private static readonly string[] StringMethodsToCheck = ["Contains", "StartsWith", "EndsWith"]; + + private static readonly LocalizableResourceString Title = new(nameof(Resources.IgnoreStringMethodReturnValueTitle), Resources.ResourceManager, typeof(Resources)); + private static readonly LocalizableResourceString MessageFormat = new(nameof(Resources.IgnoreStringMethodReturnValueMessageFormat), Resources.ResourceManager, typeof(Resources)); + private static readonly LocalizableResourceString Description = new(nameof(Resources.IgnoreStringMethodReturnValueDescription), Resources.ResourceManager, typeof(Resources)); + + internal static readonly DiagnosticDescriptor Rule = DiagnosticDescriptorHelper.Create( + DiagnosticIds.IgnoreStringMethodReturnValueRuleId, + Title, + MessageFormat, + Description, + Category.Usage, + DiagnosticSeverity.Warning, + isEnabledByDefault: true); + + /// + public override ImmutableArray SupportedDiagnostics { get; } + = ImmutableArray.Create(Rule); + + /// + public override void Initialize(AnalysisContext context) + { + context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); + context.EnableConcurrentExecution(); + context.RegisterOperationAction(AnalyzeExpressionStatement, OperationKind.ExpressionStatement); + } + + private static void AnalyzeExpressionStatement(OperationAnalysisContext context) + { + var expressionStatementOperation = (IExpressionStatementOperation)context.Operation; + + if (expressionStatementOperation.Operation is not IInvocationOperation invocationOperation) + { + return; + } + + // Check if this is a call to a string method we care about + if (!IsStringMethodCall(invocationOperation)) + { + return; + } + + string methodName = invocationOperation.TargetMethod.Name; + context.ReportDiagnostic(invocationOperation.CreateDiagnostic(Rule, methodName)); + } + + private static bool IsStringMethodCall(IInvocationOperation invocationOperation) + { + if (invocationOperation.TargetMethod.ContainingType?.SpecialType != SpecialType.System_String) + { + return false; + } + + string methodName = invocationOperation.TargetMethod.Name; + return StringMethodsToCheck.Contains(methodName); + } +} diff --git a/src/Analyzers/MSTest.Analyzers/MSTest.Analyzers.csproj b/src/Analyzers/MSTest.Analyzers/MSTest.Analyzers.csproj index 217d171183..cc4ad8f545 100644 --- a/src/Analyzers/MSTest.Analyzers/MSTest.Analyzers.csproj +++ b/src/Analyzers/MSTest.Analyzers/MSTest.Analyzers.csproj @@ -24,9 +24,4 @@ - - - - - diff --git a/src/Analyzers/MSTest.Analyzers/NonNullableReferenceNotInitializedSuppressor.cs b/src/Analyzers/MSTest.Analyzers/NonNullableReferenceNotInitializedSuppressor.cs index ccb701e511..0b866fc77a 100644 --- a/src/Analyzers/MSTest.Analyzers/NonNullableReferenceNotInitializedSuppressor.cs +++ b/src/Analyzers/MSTest.Analyzers/NonNullableReferenceNotInitializedSuppressor.cs @@ -41,19 +41,28 @@ public override void ReportSuppressions(SuppressionAnalysisContext context) foreach (Diagnostic diagnostic in context.ReportedDiagnostics) { - // The diagnostic is reported on the test method - if (diagnostic.Location.SourceTree is not { } tree) + // The main diagnostic location isn't always pointing to the TestContext property. + // It can point to the constructor. + // The additional locations will have the right thing. + // See https://github.com/dotnet/roslyn/issues/79188#issuecomment-3017087900. + // It was an intentional design to include the additional locations specifically for DiagnosticSuppressor scenarios. + // So it is safe to use AdditionalLocations here. We are not relying on an implementation detail here. + // However, we still fallback to diagnostic.Location just in case Roslyn regresses the AdditionalLocations behavior. + // Such regression happened in the past in Roslyn. + // See https://github.com/dotnet/roslyn/issues/66037 + Location location = diagnostic.AdditionalLocations.Count >= 1 ? diagnostic.AdditionalLocations[0] : diagnostic.Location; + if (location.SourceTree is not { } tree) { continue; } SyntaxNode root = tree.GetRoot(context.CancellationToken); - SyntaxNode node = root.FindNode(diagnostic.Location.SourceSpan, getInnermostNodeForTie: true); + SyntaxNode node = root.FindNode(location.SourceSpan, getInnermostNodeForTie: true); SemanticModel semanticModel = context.GetSemanticModel(tree); ISymbol? declaredSymbol = semanticModel.GetDeclaredSymbol(node, context.CancellationToken); if (declaredSymbol is IPropertySymbol property - && string.Equals(property.Name, "TestContext", StringComparison.OrdinalIgnoreCase) + && string.Equals(property.Name, "TestContext", StringComparison.Ordinal) && SymbolEqualityComparer.Default.Equals(testContextSymbol, property.GetMethod?.ReturnType) && property.ContainingType.GetAttributes().Any(attr => attr.AttributeClass.Inherits(testClassAttributeSymbol))) { diff --git a/src/Analyzers/MSTest.Analyzers/PreferAssertFailOverAlwaysFalseConditionsAnalyzer.cs b/src/Analyzers/MSTest.Analyzers/PreferAssertFailOverAlwaysFalseConditionsAnalyzer.cs index d06a95c547..ba18ade548 100644 --- a/src/Analyzers/MSTest.Analyzers/PreferAssertFailOverAlwaysFalseConditionsAnalyzer.cs +++ b/src/Analyzers/MSTest.Analyzers/PreferAssertFailOverAlwaysFalseConditionsAnalyzer.cs @@ -127,7 +127,7 @@ private static bool IsNotNullableType(IArgumentOperation valueArgumentOperation) { ITypeSymbol? valueArgType = valueArgumentOperation.Value.GetReferencedMemberOrLocalOrParameter().GetReferencedMemberOrLocalOrParameter(); return valueArgType is not null - && valueArgType.NullableAnnotation == NullableAnnotation.NotAnnotated + && valueArgType.IsValueType && valueArgType.OriginalDefinition.SpecialType != SpecialType.System_Nullable_T; } diff --git a/src/Analyzers/MSTest.Analyzers/PreferTestMethodOverDataTestMethodAnalyzer.cs b/src/Analyzers/MSTest.Analyzers/PreferTestMethodOverDataTestMethodAnalyzer.cs new file mode 100644 index 0000000000..faf8481797 --- /dev/null +++ b/src/Analyzers/MSTest.Analyzers/PreferTestMethodOverDataTestMethodAnalyzer.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; + +/// +/// MSTEST0044: . +/// +[DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] +public sealed class PreferTestMethodOverDataTestMethodAnalyzer : DiagnosticAnalyzer +{ + internal static readonly DiagnosticDescriptor PreferTestMethodOverDataTestMethodRule = DiagnosticDescriptorHelper.Create( + DiagnosticIds.PreferTestMethodOverDataTestMethodRuleId, + title: new LocalizableResourceString(nameof(Resources.PreferTestMethodOverDataTestMethodAnalyzerTitle), Resources.ResourceManager, typeof(Resources)), + messageFormat: new LocalizableResourceString(nameof(Resources.PreferTestMethodOverDataTestMethodAnalyzerMessageFormat), Resources.ResourceManager, typeof(Resources)), + description: new LocalizableResourceString(nameof(Resources.PreferTestMethodOverDataTestMethodAnalyzerDescription), Resources.ResourceManager, typeof(Resources)), + Category.Design, + DiagnosticSeverity.Warning, + isEnabledByDefault: true); + + /// + public override ImmutableArray SupportedDiagnostics { get; } + = ImmutableArray.Create(PreferTestMethodOverDataTestMethodRule); + + /// + public override void Initialize(AnalysisContext context) + { + context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); + context.EnableConcurrentExecution(); + + context.RegisterCompilationStartAction(context => + { + if (!context.Compilation.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftVisualStudioTestToolsUnitTestingDataTestMethodAttribute, out INamedTypeSymbol? dataTestMethodAttributeSymbol)) + { + return; + } + + context.RegisterSymbolAction(context => AnalyzeMethod(context, dataTestMethodAttributeSymbol), SymbolKind.Method); + context.RegisterSymbolAction(context => AnalyzeNamedType(context, dataTestMethodAttributeSymbol), SymbolKind.NamedType); + }); + } + + private static void AnalyzeMethod(SymbolAnalysisContext context, INamedTypeSymbol dataTestMethodAttributeSymbol) + { + var methodSymbol = (IMethodSymbol)context.Symbol; + + foreach (AttributeData attribute in methodSymbol.GetAttributes()) + { + // Only report on direct application of DataTestMethodAttribute, not inherited ones + if (SymbolEqualityComparer.Default.Equals(attribute.AttributeClass, dataTestMethodAttributeSymbol)) + { + if (attribute.ApplicationSyntaxReference is { } syntaxRef) + { + context.ReportDiagnostic(syntaxRef.CreateDiagnostic(PreferTestMethodOverDataTestMethodRule, context.CancellationToken)); + } + } + } + } + + private static void AnalyzeNamedType(SymbolAnalysisContext context, INamedTypeSymbol dataTestMethodAttributeSymbol) + { + var namedTypeSymbol = (INamedTypeSymbol)context.Symbol; + + // Check if this type directly inherits from DataTestMethodAttribute + if (dataTestMethodAttributeSymbol.Equals(namedTypeSymbol.BaseType, SymbolEqualityComparer.Default)) + { + context.ReportDiagnostic(namedTypeSymbol.CreateDiagnostic(PreferTestMethodOverDataTestMethodRule)); + } + } +} diff --git a/src/Analyzers/MSTest.Analyzers/PublicAPI.Shipped.txt b/src/Analyzers/MSTest.Analyzers/PublicAPI.Shipped.txt deleted file mode 100644 index 56544445f9..0000000000 --- a/src/Analyzers/MSTest.Analyzers/PublicAPI.Shipped.txt +++ /dev/null @@ -1,157 +0,0 @@ -#nullable enable -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.AvoidAssertAreSameWithValueTypesAnalyzer -MSTest.Analyzers.AvoidAssertAreSameWithValueTypesAnalyzer.AvoidAssertAreSameWithValueTypesAnalyzer() -> void -MSTest.Analyzers.AvoidExpectedExceptionAttributeAnalyzer -MSTest.Analyzers.AvoidExpectedExceptionAttributeAnalyzer.AvoidExpectedExceptionAttributeAnalyzer() -> void -MSTest.Analyzers.AvoidUsingAssertsInAsyncVoidContextAnalyzer -MSTest.Analyzers.AvoidUsingAssertsInAsyncVoidContextAnalyzer.AvoidUsingAssertsInAsyncVoidContextAnalyzer() -> 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.DoNotUseShadowingAnalyzer -MSTest.Analyzers.DoNotUseShadowingAnalyzer.DoNotUseShadowingAnalyzer() -> void -MSTest.Analyzers.DoNotUseSystemDescriptionAttributeAnalyzer -MSTest.Analyzers.DoNotUseSystemDescriptionAttributeAnalyzer.DoNotUseSystemDescriptionAttributeAnalyzer() -> void -MSTest.Analyzers.DynamicDataShouldBeValidAnalyzer -MSTest.Analyzers.DynamicDataShouldBeValidAnalyzer.DynamicDataShouldBeValidAnalyzer() -> void -MSTest.Analyzers.NonNullableReferenceNotInitializedSuppressor -MSTest.Analyzers.NonNullableReferenceNotInitializedSuppressor.NonNullableReferenceNotInitializedSuppressor() -> 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.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.ReviewAlwaysTrueAssertConditionAnalyzer -MSTest.Analyzers.ReviewAlwaysTrueAssertConditionAnalyzer.ReviewAlwaysTrueAssertConditionAnalyzer() -> 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.UseClassCleanupBehaviorEndOfClassAnalyzer -MSTest.Analyzers.UseClassCleanupBehaviorEndOfClassAnalyzer.UseClassCleanupBehaviorEndOfClassAnalyzer() -> void -MSTest.Analyzers.UseConditionBaseWithTestClassAnalyzer -MSTest.Analyzers.UseConditionBaseWithTestClassAnalyzer.UseConditionBaseWithTestClassAnalyzer() -> void -MSTest.Analyzers.UseDeploymentItemWithTestMethodOrTestClassAnalyzer -MSTest.Analyzers.UseDeploymentItemWithTestMethodOrTestClassAnalyzer.UseDeploymentItemWithTestMethodOrTestClassAnalyzer() -> 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.AvoidAssertAreSameWithValueTypesAnalyzer.Initialize(Microsoft.CodeAnalysis.Diagnostics.AnalysisContext! context) -> void -override MSTest.Analyzers.AvoidAssertAreSameWithValueTypesAnalyzer.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.AvoidUsingAssertsInAsyncVoidContextAnalyzer.Initialize(Microsoft.CodeAnalysis.Diagnostics.AnalysisContext! context) -> void -override MSTest.Analyzers.AvoidUsingAssertsInAsyncVoidContextAnalyzer.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.DoNotUseShadowingAnalyzer.Initialize(Microsoft.CodeAnalysis.Diagnostics.AnalysisContext! context) -> void -override MSTest.Analyzers.DoNotUseShadowingAnalyzer.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.DynamicDataShouldBeValidAnalyzer.Initialize(Microsoft.CodeAnalysis.Diagnostics.AnalysisContext! context) -> void -override MSTest.Analyzers.DynamicDataShouldBeValidAnalyzer.SupportedDiagnostics.get -> System.Collections.Immutable.ImmutableArray -override MSTest.Analyzers.NonNullableReferenceNotInitializedSuppressor.ReportSuppressions(Microsoft.CodeAnalysis.Diagnostics.SuppressionAnalysisContext context) -> void -override MSTest.Analyzers.NonNullableReferenceNotInitializedSuppressor.SupportedSuppressions.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.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.ReviewAlwaysTrueAssertConditionAnalyzer.Initialize(Microsoft.CodeAnalysis.Diagnostics.AnalysisContext! context) -> void -override MSTest.Analyzers.ReviewAlwaysTrueAssertConditionAnalyzer.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.UseClassCleanupBehaviorEndOfClassAnalyzer.Initialize(Microsoft.CodeAnalysis.Diagnostics.AnalysisContext! context) -> void -override MSTest.Analyzers.UseClassCleanupBehaviorEndOfClassAnalyzer.SupportedDiagnostics.get -> System.Collections.Immutable.ImmutableArray -override MSTest.Analyzers.UseConditionBaseWithTestClassAnalyzer.Initialize(Microsoft.CodeAnalysis.Diagnostics.AnalysisContext! context) -> void -override MSTest.Analyzers.UseConditionBaseWithTestClassAnalyzer.SupportedDiagnostics.get -> System.Collections.Immutable.ImmutableArray -override MSTest.Analyzers.UseDeploymentItemWithTestMethodOrTestClassAnalyzer.Initialize(Microsoft.CodeAnalysis.Diagnostics.AnalysisContext! context) -> void -override MSTest.Analyzers.UseDeploymentItemWithTestMethodOrTestClassAnalyzer.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 diff --git a/src/Analyzers/MSTest.Analyzers/PublicAPI.Unshipped.txt b/src/Analyzers/MSTest.Analyzers/PublicAPI.Unshipped.txt deleted file mode 100644 index ffbf652d09..0000000000 --- a/src/Analyzers/MSTest.Analyzers/PublicAPI.Unshipped.txt +++ /dev/null @@ -1,9 +0,0 @@ -#nullable enable -MSTest.Analyzers.DuplicateDataRowAnalyzer -MSTest.Analyzers.DuplicateDataRowAnalyzer.DuplicateDataRowAnalyzer() -> void -MSTest.Analyzers.UseRetryWithTestMethodAnalyzer -MSTest.Analyzers.UseRetryWithTestMethodAnalyzer.UseRetryWithTestMethodAnalyzer() -> void -override MSTest.Analyzers.DuplicateDataRowAnalyzer.Initialize(Microsoft.CodeAnalysis.Diagnostics.AnalysisContext! context) -> void -override MSTest.Analyzers.DuplicateDataRowAnalyzer.SupportedDiagnostics.get -> System.Collections.Immutable.ImmutableArray -override MSTest.Analyzers.UseRetryWithTestMethodAnalyzer.Initialize(Microsoft.CodeAnalysis.Diagnostics.AnalysisContext! context) -> void -override MSTest.Analyzers.UseRetryWithTestMethodAnalyzer.SupportedDiagnostics.get -> System.Collections.Immutable.ImmutableArray diff --git a/src/Analyzers/MSTest.Analyzers/PublicTypeShouldBeTestClassAnalyzer.cs b/src/Analyzers/MSTest.Analyzers/PublicTypeShouldBeTestClassAnalyzer.cs index ae2e53ad40..4fc74ef968 100644 --- a/src/Analyzers/MSTest.Analyzers/PublicTypeShouldBeTestClassAnalyzer.cs +++ b/src/Analyzers/MSTest.Analyzers/PublicTypeShouldBeTestClassAnalyzer.cs @@ -63,7 +63,7 @@ private static void AnalyzeSymbol(SymbolAnalysisContext context, INamedTypeSymbo } // The type is public, ensure this is a test class. - if (!namedTypeSymbol.GetAttributes().Any(attr => SymbolEqualityComparer.Default.Equals(attr.AttributeClass, testClassAttributeSymbol))) + if (!namedTypeSymbol.GetAttributes().Any(attr => attr.AttributeClass.Inherits(testClassAttributeSymbol))) { context.ReportDiagnostic(namedTypeSymbol.CreateDiagnostic(Rule, namedTypeSymbol.Name)); } diff --git a/src/Analyzers/MSTest.Analyzers/Resources.Designer.cs b/src/Analyzers/MSTest.Analyzers/Resources.Designer.cs deleted file mode 100644 index 46445d28d4..0000000000 --- a/src/Analyzers/MSTest.Analyzers/Resources.Designer.cs +++ /dev/null @@ -1,1216 +0,0 @@ -//------------------------------------------------------------------------------ -// -// 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; - - - /// - /// 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 Resources { - - private static global::System.Resources.ResourceManager resourceMan; - - private static global::System.Globalization.CultureInfo resourceCulture; - - [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] - internal Resources() { - } - - /// - /// 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.Resources", typeof(Resources).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 Methods marked with '[AssemblyCleanup]' should follow the following layout to be valid: - ///-it can't be declared on a generic class - ///-it should be 'public' - ///-it should be 'static' - ///-it should not be 'async void' - ///-it should not be a special method (finalizer, operator...). - ///-it should not be generic - ///-it should either not take any parameter, or take a single parameter of type 'TestContext' - ///-return type should be 'void', 'Task' or 'ValueTask' - /// - ///The type declaring these methods should also respect the followi [rest of string was truncated]";. - /// - internal static string AssemblyCleanupShouldBeValidDescription { - get { - return ResourceManager.GetString("AssemblyCleanupShouldBeValidDescription", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to AssemblyCleanup method '{0}' signature is invalid. - /// - internal static string AssemblyCleanupShouldBeValidMessageFormat { - get { - return ResourceManager.GetString("AssemblyCleanupShouldBeValidMessageFormat", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to AssemblyCleanup methods should have valid layout. - /// - internal static string AssemblyCleanupShouldBeValidTitle { - get { - return ResourceManager.GetString("AssemblyCleanupShouldBeValidTitle", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Methods marked with '[AssemblyInitialize]' should follow the following layout to be valid: - ///-it can't be declared on a generic class - ///-it should be 'public' - ///-it should be 'static' - ///-it should not be 'async void' - ///-it should not be a special method (finalizer, operator...). - ///-it should not be generic - ///-it should take one parameter of type 'TestContext' - ///-return type should be 'void', 'Task' or 'ValueTask' - /// - ///The type declaring these methods should also respect the following rules: - ///-The type should be a cla [rest of string was truncated]";. - /// - internal static string AssemblyInitializeShouldBeValidDescription { - get { - return ResourceManager.GetString("AssemblyInitializeShouldBeValidDescription", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to AssemblyInitialize method '{0}' signature is invalid. - /// - internal static string AssemblyInitializeShouldBeValidMessageFormat { - get { - return ResourceManager.GetString("AssemblyInitializeShouldBeValidMessageFormat", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to AssemblyInitialize methods should have valid layout. - /// - internal static string AssemblyInitializeShouldBeValidTitle { - get { - return ResourceManager.GetString("AssemblyInitializeShouldBeValidTitle", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Prefer adding an additional assertion that checks for null. - /// - internal static string AssertionArgsShouldAvoidConditionalAccessMessageFormat { - get { - return ResourceManager.GetString("AssertionArgsShouldAvoidConditionalAccessMessageFormat", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Avoid conditional access in assertions. - /// - internal static string AssertionArgsShouldAvoidConditionalAccessTitle { - get { - return ResourceManager.GetString("AssertionArgsShouldAvoidConditionalAccessTitle", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to '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.. - /// - internal static string AssertionArgsShouldBePassedInCorrectOrderDescription { - get { - return ResourceManager.GetString("AssertionArgsShouldBePassedInCorrectOrderDescription", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Assertion arguments should be passed in the correct order. 'actual' and 'expected'/'notExpected' arguments have been swapped.. - /// - internal static string AssertionArgsShouldBePassedInCorrectOrderMessageFormat { - get { - return ResourceManager.GetString("AssertionArgsShouldBePassedInCorrectOrderMessageFormat", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Assertion arguments should be passed in the correct order. - /// - internal static string AssertionArgsShouldBePassedInCorrectOrderTitle { - get { - return ResourceManager.GetString("AssertionArgsShouldBePassedInCorrectOrderTitle", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Use 'Assert.AreEqual'/'Assert.AreNotEqual' instead of 'Assert.AreSame'/'Assert.AreNotSame' when comparing value types. Passing a value type to 'Assert.AreSame'/'Assert.AreNotSame' will be boxed (creating a new object). Because 'Assert.AreSame'/'Assert.AreNotSame' does the comparison by reference, 'Assert.AreSame' will fail when boxing happens, and 'Assert.AreNotSame' will always pass.. - /// - internal static string AvoidAssertAreSameWithValueTypesDescription { - get { - return ResourceManager.GetString("AvoidAssertAreSameWithValueTypesDescription", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Use '{0}' instead of '{1}' when comparing value types. - /// - internal static string AvoidAssertAreSameWithValueTypesMessageFormat { - get { - return ResourceManager.GetString("AvoidAssertAreSameWithValueTypesMessageFormat", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Don't use 'Assert.AreSame' or 'Assert.AreNotSame' with value types. - /// - internal static string AvoidAssertAreSameWithValueTypesTitle { - get { - return ResourceManager.GetString("AvoidAssertAreSameWithValueTypesTitle", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Prefer 'Assert.ThrowsExactly' or 'Assert.ThrowsExactlyAsync' over '[ExpectedException]' as it ensures that only the expected call throws the expected exception. The assert APIs also provide more flexibility and allow you to assert extra properties of the exception.. - /// - internal static string AvoidExpectedExceptionAttributeDescription { - get { - return ResourceManager.GetString("AvoidExpectedExceptionAttributeDescription", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Prefer 'Assert.ThrowsExactly/ThrowsExactlyAsync' over '[ExpectedException]'. - /// - internal static string AvoidExpectedExceptionAttributeMessageFormat { - get { - return ResourceManager.GetString("AvoidExpectedExceptionAttributeMessageFormat", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Avoid '[ExpectedException]'. - /// - internal static string AvoidExpectedExceptionAttributeTitle { - get { - return ResourceManager.GetString("AvoidExpectedExceptionAttributeTitle", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Do not assert inside 'async void' methods, local functions, or lambdas. Exceptions that are thrown in this context will be unhandled exceptions. When using VSTest under .NET Framework, they will be silently swallowed. When using Microsoft.Testing.Platform or VSTest under modern .NET, they may crash the process.. - /// - internal static string AvoidUsingAssertsInAsyncVoidContextDescription { - get { - return ResourceManager.GetString("AvoidUsingAssertsInAsyncVoidContextDescription", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Do not assert inside 'async void' methods, local functions, or lambdas because they may not fail the test. - /// - internal static string AvoidUsingAssertsInAsyncVoidContextMessageFormat { - get { - return ResourceManager.GetString("AvoidUsingAssertsInAsyncVoidContextMessageFormat", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Do not assert inside 'async void' contexts. - /// - internal static string AvoidUsingAssertsInAsyncVoidContextTitle { - get { - return ResourceManager.GetString("AvoidUsingAssertsInAsyncVoidContextTitle", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Methods marked with '[ClassCleanup]' should follow the following layout to be valid: - ///-it can't be declared on a generic class without the 'InheritanceBehavior' mode is set - ///-it should be 'public' - ///-it should be 'static' - ///-it should not be 'async void' - ///-it should not be a special method (finalizer, operator...). - ///-it should not be generic - ///-it should either not take any parameter, or take a single parameter of type 'TestContext' - ///-return type should be 'void', 'Task' or 'ValueTask' - ///-'InheritanceBehavior.B [rest of string was truncated]";. - /// - internal static string ClassCleanupShouldBeValidDescription { - get { - return ResourceManager.GetString("ClassCleanupShouldBeValidDescription", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to ClassCleanup method '{0}' signature is invalid. - /// - internal static string ClassCleanupShouldBeValidMessageFormat { - get { - return ResourceManager.GetString("ClassCleanupShouldBeValidMessageFormat", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to ClassCleanup methods should have valid layout. - /// - internal static string ClassCleanupShouldBeValidTitle { - get { - return ResourceManager.GetString("ClassCleanupShouldBeValidTitle", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Methods marked with '[ClassInitialize]' should follow the following layout to be valid: - ///-it can't be declared on a generic class without the 'InheritanceBehavior' mode is set - ///-it should be 'public' - ///-it should be 'static' - ///-it should not be 'async void' - ///-it should not be a special method (finalizer, operator...). - ///-it should not be generic - ///-it should take one parameter of type 'TestContext' - ///-return type should be 'void', 'Task' or 'ValueTask' - ///-'InheritanceBehavior.BeforeEachDerivedClass' attribute par [rest of string was truncated]";. - /// - internal static string ClassInitializeShouldBeValidDescription { - get { - return ResourceManager.GetString("ClassInitializeShouldBeValidDescription", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to ClassInitialize method '{0}' signature is invalid. - /// - internal static string ClassInitializeShouldBeValidMessageFormat { - get { - return ResourceManager.GetString("ClassInitializeShouldBeValidMessageFormat", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to ClassInitialize methods should have valid layout. - /// - internal static string ClassInitializeShouldBeValidTitle { - get { - return ResourceManager.GetString("ClassInitializeShouldBeValidTitle", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to DataRow entry should have the following layout to be valid: - ///- should only be set on a test method; - ///- argument count should match method argument count; - ///- argument type should match method argument type.. - /// - internal static string DataRowShouldBeValidDescription { - get { - return ResourceManager.GetString("DataRowShouldBeValidDescription", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to DataRow argument count should match method parameter count (constructor arguments: {0}, method parameters: {1}). - /// - internal static string DataRowShouldBeValidMessageFormat_ArgumentCountMismatch { - get { - return ResourceManager.GetString("DataRowShouldBeValidMessageFormat_ArgumentCountMismatch", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to DataRow argument type should match method parameter type. Mismatches occur at indices: {0}. - /// - internal static string DataRowShouldBeValidMessageFormat_ArgumentTypeMismatch { - get { - return ResourceManager.GetString("DataRowShouldBeValidMessageFormat_ArgumentTypeMismatch", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Found two conflicting types for generic parameter '{0}'. The conflicting types are '{1}' and '{2}'.. - /// - internal static string DataRowShouldBeValidMessageFormat_GenericTypeArgumentConflictingTypes { - get { - return ResourceManager.GetString("DataRowShouldBeValidMessageFormat_GenericTypeArgumentConflictingTypes", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to The type of the generic parameter '{0}' could not be inferred.. - /// - internal static string DataRowShouldBeValidMessageFormat_GenericTypeArgumentNotResolved { - get { - return ResourceManager.GetString("DataRowShouldBeValidMessageFormat_GenericTypeArgumentNotResolved", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to DataRow should only be set on a test method. - /// - internal static string DataRowShouldBeValidMessageFormat_OnTestMethod { - get { - return ResourceManager.GetString("DataRowShouldBeValidMessageFormat_OnTestMethod", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to DataRow should be valid. - /// - internal static string DataRowShouldBeValidTitle { - get { - return ResourceManager.GetString("DataRowShouldBeValidTitle", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Do not negate boolean assertions, instead use the opposite assertion. - /// - internal static string DoNotNegateBooleanAssertionMessageFormat { - get { - return ResourceManager.GetString("DoNotNegateBooleanAssertionMessageFormat", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Do not negate boolean assertions. - /// - internal static string DoNotNegateBooleanAssertionTitle { - get { - return ResourceManager.GetString("DoNotNegateBooleanAssertionTitle", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Do not store TestContext in a static member. - /// - internal static string DoNotStoreStaticTestContextAnalyzerMessageFormat { - get { - return ResourceManager.GetString("DoNotStoreStaticTestContextAnalyzerMessageFormat", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Do not store TestContext in a static member. - /// - internal static string DoNotStoreStaticTestContextAnalyzerTitle { - get { - return ResourceManager.GetString("DoNotStoreStaticTestContextAnalyzerTitle", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Shadowing test members could cause testing issues (such as NRE).. - /// - internal static string DoNotUseShadowingDescription { - get { - return ResourceManager.GetString("DoNotUseShadowingDescription", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Member '{0}' already exists in the base class. - /// - internal static string DoNotUseShadowingMessageFormat { - get { - return ResourceManager.GetString("DoNotUseShadowingMessageFormat", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Do not use shadowing. - /// - internal static string DoNotUseShadowingTitle { - get { - return ResourceManager.GetString("DoNotUseShadowingTitle", resourceCulture); - } - } - - /// - /// 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 Do not duplicate 'DataRow' attributes. This is usually a copy/paste error. The attribute indices are '{0}' and '{1}'.. - /// - internal static string DuplicateDataRowMessageFormat { - get { - return ResourceManager.GetString("DuplicateDataRowMessageFormat", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Avoid duplicated 'DataRow' entries. - /// - internal static string DuplicateDataRowTitle { - get { - return ResourceManager.GetString("DuplicateDataRowTitle", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to 'DynamicData' entry should have the following layout to be valid: - ///- should only be set on a test method; - ///- member should be defined on the type specified; - ///- member should be a method if DynamicDataSourceType.Method is specified or a property otherwise.. - /// - internal static string DynamicDataShouldBeValidDescription { - get { - return ResourceManager.GetString("DynamicDataShouldBeValidDescription", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to '[DynamicData]' data member '{0}.{1}' signature is invalid. - /// - internal static string DynamicDataShouldBeValidMessageFormat_DataMemberSignature { - get { - return ResourceManager.GetString("DynamicDataShouldBeValidMessageFormat_DataMemberSignature", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to '[DynamicData]' display name method '{0}.{1}' signature is invalid. - /// - internal static string DynamicDataShouldBeValidMessageFormat_DisplayMethodSignature { - get { - return ResourceManager.GetString("DynamicDataShouldBeValidMessageFormat_DisplayMethodSignature", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to '[DynamicData]' member '{0}.{1}' should be a method. - /// - internal static string DynamicDataShouldBeValidMessageFormat_MemberMethod { - get { - return ResourceManager.GetString("DynamicDataShouldBeValidMessageFormat_MemberMethod", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to '[DynamicData]' member '{0}.{1}' cannot be found. - /// - internal static string DynamicDataShouldBeValidMessageFormat_MemberNotFound { - get { - return ResourceManager.GetString("DynamicDataShouldBeValidMessageFormat_MemberNotFound", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to '[DynamicData]' referenced member '{0}.{1}' should return 'IEnumerable<object[]>', 'IEnumerable<Tuple>` or 'IEnumerable<ValueTuple>'. - /// - internal static string DynamicDataShouldBeValidMessageFormat_MemberType { - get { - return ResourceManager.GetString("DynamicDataShouldBeValidMessageFormat_MemberType", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to '[DynamicData]' should only be set on a test method. - /// - internal static string DynamicDataShouldBeValidMessageFormat_OnTestMethod { - get { - return ResourceManager.GetString("DynamicDataShouldBeValidMessageFormat_OnTestMethod", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to '[DynamicData]' member '{0}.{1}' is a method so you should use 'DynamicDataSourceType.AutoDetect' or 'DynamicDataSourceType.Method' (auto detect is the default when not specified explicitly, and is recommended). - /// - internal static string DynamicDataShouldBeValidMessageFormat_SourceTypeMethod { - get { - return ResourceManager.GetString("DynamicDataShouldBeValidMessageFormat_SourceTypeMethod", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to '[DynamicData]' member '{0}.{1}' is not a property nor a method. Only properties and methods are supported.. - /// - internal static string DynamicDataShouldBeValidMessageFormat_SourceTypeNotPropertyOrMethod { - get { - return ResourceManager.GetString("DynamicDataShouldBeValidMessageFormat_SourceTypeNotPropertyOrMethod", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to '[DynamicData]' member '{0}.{1}' is a property so you should use 'DynamicDataSourceType.AutoDetect' or 'DynamicDataSourceType.Property' (auto detect is the default when not specified explicitly, and is recommended). - /// - internal static string DynamicDataShouldBeValidMessageFormat_SourceTypeProperty { - get { - return ResourceManager.GetString("DynamicDataShouldBeValidMessageFormat_SourceTypeProperty", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to '[DynamicData]' member '{0}.{1}' is found more than once. - /// - internal static string DynamicDataShouldBeValidMessageFormat_TooManyMembers { - get { - return ResourceManager.GetString("DynamicDataShouldBeValidMessageFormat_TooManyMembers", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to DynamicData should be valid. - /// - internal static string DynamicDataShouldBeValidTitle { - get { - return ResourceManager.GetString("DynamicDataShouldBeValidTitle", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Use 'Assert.Fail' instead of an always-failing 'Assert.{0}' assert. - /// - internal static string PreferAssertFailOverAlwaysFalseConditionsMessageFormat { - get { - return ResourceManager.GetString("PreferAssertFailOverAlwaysFalseConditionsMessageFormat", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Use 'Assert.Fail' instead of an always-failing assert. - /// - internal static string PreferAssertFailOverAlwaysFalseConditionsTitle { - get { - return ResourceManager.GetString("PreferAssertFailOverAlwaysFalseConditionsTitle", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Prefer constructors over TestInitialize methods. - /// - internal static string PreferConstructorOverTestInitializeMessageFormat { - get { - return ResourceManager.GetString("PreferConstructorOverTestInitializeMessageFormat", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Prefer constructors over TestInitialize methods. - /// - internal static string PreferConstructorOverTestInitializeTitle { - get { - return ResourceManager.GetString("PreferConstructorOverTestInitializeTitle", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Prefer 'Dispose' over TestCleanup methods. - /// - internal static string PreferDisposeOverTestCleanupMessageFormat { - get { - return ResourceManager.GetString("PreferDisposeOverTestCleanupMessageFormat", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Prefer 'Dispose' over TestCleanup methods. - /// - internal static string PreferDisposeOverTestCleanupTitle { - get { - return ResourceManager.GetString("PreferDisposeOverTestCleanupTitle", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Prefer TestCleanup over 'Dispose' methods. - /// - internal static string PreferTestCleanupOverDisposeMessageFormat { - get { - return ResourceManager.GetString("PreferTestCleanupOverDisposeMessageFormat", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Prefer TestCleanup over 'Dispose' methods. - /// - internal static string PreferTestCleanupOverDisposeTitle { - get { - return ResourceManager.GetString("PreferTestCleanupOverDisposeTitle", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Prefer TestInitialize methods over constructors. - /// - internal static string PreferTestInitializeOverConstructorMessageFormat { - get { - return ResourceManager.GetString("PreferTestInitializeOverConstructorMessageFormat", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Prefer TestInitialize methods over constructors. - /// - internal static string PreferTestInitializeOverConstructorTitle { - get { - return ResourceManager.GetString("PreferTestInitializeOverConstructorTitle", resourceCulture); - } - } - - /// - /// 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.. - /// - internal static string PublicTypeShouldBeTestClassDescription { - get { - return ResourceManager.GetString("PublicTypeShouldBeTestClassDescription", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Public type '{0}' should be marked with '[TestClass]' or changed to 'internal'. - /// - internal static string PublicTypeShouldBeTestClassMessageFormat { - get { - return ResourceManager.GetString("PublicTypeShouldBeTestClassMessageFormat", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Public types should be test classes. - /// - internal static string PublicTypeShouldBeTestClassTitle { - get { - return ResourceManager.GetString("PublicTypeShouldBeTestClassTitle", resourceCulture); - } - } - - /// - /// 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) - ///- it should not be 'static' (except if it contains only 'AssemblyInitialize' and/or 'AssemblyCleanup' methods) - ///- it should not be generic.. - /// - internal static string TestClassShouldBeValidDescription { - get { - return ResourceManager.GetString("TestClassShouldBeValidDescription", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Test class '{0}' should be valid. - /// - internal static string TestClassShouldBeValidMessageFormat { - get { - return ResourceManager.GetString("TestClassShouldBeValidMessageFormat", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Test classes should have valid layout. - /// - internal static string TestClassShouldBeValidTitle { - get { - return ResourceManager.GetString("TestClassShouldBeValidTitle", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Test class should have at least one test method or be 'static' with method(s) marked by '[AssemblyInitialize]' and/or '[AssemblyCleanup]'.. - /// - internal static string TestClassShouldHaveTestMethodDescription { - get { - return ResourceManager.GetString("TestClassShouldHaveTestMethodDescription", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Test class '{0}' should have at least one test method or be 'static' with method(s) marked by '[AssemblyInitialize]' and/or '[AssemblyCleanup]'. - /// - internal static string TestClassShouldHaveTestMethodMessageFormat { - get { - return ResourceManager.GetString("TestClassShouldHaveTestMethodMessageFormat", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Test class should have test method. - /// - internal static string TestClassShouldHaveTestMethodTitle { - get { - return ResourceManager.GetString("TestClassShouldHaveTestMethodTitle", resourceCulture); - } - } - - /// - /// 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 'abstract' - ///-it should not be 'async void' - ///-it should not be 'static' - ///-it should not be a special method (finalizer, operator...). - ///-it should not be generic - ///-it should not take any parameter - ///-return type should be 'void', 'Task' or 'ValueTask' - /// - ///The type declaring these methods should also respect the following rules: - ///-The type should be a class - ///-The class should be 'public' [rest of string was truncated]";. - /// - internal static string TestCleanupShouldBeValidDescription { - get { - return ResourceManager.GetString("TestCleanupShouldBeValidDescription", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to TestCleanup method '{0}' signature is invalid. - /// - internal static string TestCleanupShouldBeValidMessageFormat { - get { - return ResourceManager.GetString("TestCleanupShouldBeValidMessageFormat", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to TestCleanup method should have valid layout. - /// - internal static string TestCleanupShouldBeValidTitle { - get { - return ResourceManager.GetString("TestCleanupShouldBeValidTitle", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to 'TestContext' should be a non-static field or property assigned in constructor or for a property set by MSTest, it should follow the layout: - ///- it should be 'public' regardless of whether '[assembly: DiscoverInternals]' attribute is set or not. - ///- it should not be 'static' - ///- it should have a setter.. - /// - internal static string TestContextShouldBeValidDescription { - get { - return ResourceManager.GetString("TestContextShouldBeValidDescription", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Property 'TestContext' should be valid. - /// - internal static string TestContextShouldBeValidMessageFormat { - get { - return ResourceManager.GetString("TestContextShouldBeValidMessageFormat", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Test context property should have valid layout. - /// - internal static string TestContextShouldBeValidTitle { - get { - return ResourceManager.GetString("TestContextShouldBeValidTitle", resourceCulture); - } - } - - /// - /// 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 'abstract' - ///-it should not be 'async void' - ///-it should not be 'static' - ///-it should not be a special method (finalizer, operator...). - ///-it should not be generic - ///-it should not take any parameter - ///-return type should be 'void', 'Task' or 'ValueTask' - /// - ///The type declaring these methods should also respect the following rules: - ///-The type should be a class - ///-The class should be 'publ [rest of string was truncated]";. - /// - internal static string TestInitializeShouldBeValidDescription { - get { - return ResourceManager.GetString("TestInitializeShouldBeValidDescription", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to TestInitialize method '{0}' signature is invalid. - /// - internal static string TestInitializeShouldBeValidMessageFormat { - get { - return ResourceManager.GetString("TestInitializeShouldBeValidMessageFormat", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to TestInitialize method should have valid layout. - /// - internal static string TestInitializeShouldBeValidTitle { - get { - return ResourceManager.GetString("TestInitializeShouldBeValidTitle", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Test methods, methods marked with the '[TestMethod]' attribute, should respect the following layout to be considered valid by MSTest: - ///- it should be 'public' (or 'internal' if '[assembly: DiscoverInternals]' attribute is set) - ///- it should not be 'static' - ///- it should may be generic as long as type parameters can be inferred and argument types are compatible - ///- it should not be 'abstract' - ///- return type should be 'void', 'Task' or 'ValueTask' - ///- it should not be 'async void' - ///- it should not be a special me [rest of string was truncated]";. - /// - internal static string TestMethodShouldBeValidDescription { - get { - return ResourceManager.GetString("TestMethodShouldBeValidDescription", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Test method '{0}' signature is invalid. - /// - internal static string TestMethodShouldBeValidMessageFormat { - get { - return ResourceManager.GetString("TestMethodShouldBeValidMessageFormat", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Test methods should have valid layout. - /// - internal static string TestMethodShouldBeValidTitle { - get { - return ResourceManager.GetString("TestMethodShouldBeValidTitle", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Test methods should not be ignored (marked with '[Ignore]').. - /// - internal static string TestMethodShouldNotBeIgnoredAnalyzerDescription { - get { - return ResourceManager.GetString("TestMethodShouldNotBeIgnoredAnalyzerDescription", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Test method '{0}' should not be ignored. - /// - internal static string TestMethodShouldNotBeIgnoredAnalyzerFormat { - get { - return ResourceManager.GetString("TestMethodShouldNotBeIgnoredAnalyzerFormat", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Test method should not be ignored. - /// - internal static string TestMethodShouldNotBeIgnoredAnalyzerTitle { - get { - return ResourceManager.GetString("TestMethodShouldNotBeIgnoredAnalyzerTitle", resourceCulture); - } - } - - /// - /// 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]. - /// - internal static string UseAttributeOnTestMethodAnalyzerMessageFormat { - get { - return ResourceManager.GetString("UseAttributeOnTestMethodAnalyzerMessageFormat", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to [{0}] can only be set on methods marked with [TestMethod]. - /// - internal static string UseAttributeOnTestMethodAnalyzerTitle { - get { - return ResourceManager.GetString("UseAttributeOnTestMethodAnalyzerTitle", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Without using 'ClassCleanupBehavior.EndOfClass', the '[ClassCleanup]' will by default be run at the end of the assembly and not at the end of the class.. - /// - internal static string UseClassCleanupBehaviorEndOfClassDescription { - get { - return ResourceManager.GetString("UseClassCleanupBehaviorEndOfClassDescription", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Use 'ClassCleanupBehavior.EndOfClass' with the '[ClassCleanup]'. - /// - internal static string UseClassCleanupBehaviorEndOfClassMessageFormat { - get { - return ResourceManager.GetString("UseClassCleanupBehaviorEndOfClassMessageFormat", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Use 'ClassCleanupBehavior.EndOfClass' with the '[ClassCleanup]'. - /// - internal static string UseClassCleanupBehaviorEndOfClassTitle { - get { - return ResourceManager.GetString("UseClassCleanupBehaviorEndOfClassTitle", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to The attribute '{0}' which derives from 'ConditionBaseAttribute' should be used only on classes marked with `TestClassAttribute`. - /// - internal static string UseConditionBaseWithTestClassMessageFormat { - get { - return ResourceManager.GetString("UseConditionBaseWithTestClassMessageFormat", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Use 'ConditionBaseAttribute' on test classes. - /// - internal static string UseConditionBaseWithTestClassTitle { - get { - return ResourceManager.GetString("UseConditionBaseWithTestClassTitle", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to '[DeploymentItem]' can be specified only on test class or test method. - /// - internal static string UseDeploymentItemWithTestMethodOrTestClassMessageFormat { - get { - return ResourceManager.GetString("UseDeploymentItemWithTestMethodOrTestClassMessageFormat", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to '[DeploymentItem]' can be specified only on test class or test method. - /// - internal static string UseDeploymentItemWithTestMethodOrTestClassTitle { - get { - return ResourceManager.GetString("UseDeploymentItemWithTestMethodOrTestClassTitle", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Use 'Assert.ThrowsExactly' instead of 'Assert.ThrowsException'. - /// - internal static string UseNewerAssertThrowsMessageFormat { - get { - return ResourceManager.GetString("UseNewerAssertThrowsMessageFormat", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Use newer methods to assert exceptions. - /// - internal static string UseNewerAssertThrowsTitle { - get { - return ResourceManager.GetString("UseNewerAssertThrowsTitle", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to By default, MSTest runs tests within the same assembly sequentially, which can lead to severe performance limitations. It is recommended to enable assembly attribute '[Parallelize]' to run tests in parallel, or if the assembly is known to not be parallelizable, to use explicitly the assembly level attribute '[DoNotParallelize]'.. - /// - internal static string UseParallelizeAttributeAnalyzerDescription { - get { - return ResourceManager.GetString("UseParallelizeAttributeAnalyzerDescription", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Explicitly enable or disable tests parallelization. - /// - internal static string UseParallelizeAttributeAnalyzerMessageFormat { - get { - return ResourceManager.GetString("UseParallelizeAttributeAnalyzerMessageFormat", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Explicitly enable or disable tests parallelization. - /// - internal static string UseParallelizeAttributeAnalyzerTitle { - get { - return ResourceManager.GetString("UseParallelizeAttributeAnalyzerTitle", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Use 'Assert.{0}' instead of 'Assert.{1}'. - /// - internal static string UseProperAssertMethodsMessageFormat { - get { - return ResourceManager.GetString("UseProperAssertMethodsMessageFormat", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Use proper 'Assert' methods. - /// - internal static string UseProperAssertMethodsTitle { - get { - return ResourceManager.GetString("UseProperAssertMethodsTitle", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to An attribute that derives from 'RetryBaseAttribute' can be specified only on a test method. - /// - internal static string UseRetryWithTestMethodMessageFormat { - get { - return ResourceManager.GetString("UseRetryWithTestMethodMessageFormat", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Use retry attribute on test method. - /// - internal static string UseRetryWithTestMethodTitle { - get { - return ResourceManager.GetString("UseRetryWithTestMethodTitle", resourceCulture); - } - } - } -} diff --git a/src/Analyzers/MSTest.Analyzers/Resources.resx b/src/Analyzers/MSTest.Analyzers/Resources.resx index 304dfec95d..1ecc9df254 100644 --- a/src/Analyzers/MSTest.Analyzers/Resources.resx +++ b/src/Analyzers/MSTest.Analyzers/Resources.resx @@ -251,7 +251,10 @@ The type declaring these methods should also respect the following rules: DataRow argument count should match method parameter count (constructor arguments: {0}, method parameters: {1}) - DataRow argument type should match method parameter type. Mismatches occur at indices: {0} + DataRow argument types do not match method parameter types. {0} + + + Parameter '{0}' expects type '{1}', but the provided value has type '{2}' DataRow should only be set on a test method @@ -426,6 +429,9 @@ The type declaring these methods should also respect the following rules: Asynchronous test methods do not require the 'Async' suffix + + TestContext parameter is required by MSTest for AssemblyInitialize and ClassInitialize methods + [{0}] can only be set on methods marked with [TestMethod] @@ -445,10 +451,10 @@ The type declaring these methods should also respect the following rules: Type containing '[TestMethod]' should be marked with '[TestClass]' - Type contaning '[TestMethod]' should be marked with '[TestClass]', otherwise the test method will be silently ignored. + Type containing '[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]' + Type '{0}' contains test methods and should be marked with '[TestClass]' 'System.ComponentModel.DescriptionAttribute' has no effect on test methods @@ -504,6 +510,9 @@ The type declaring these methods should also respect the following rules: '[DynamicData]' member '{0}.{1}' is a method so you should use 'DynamicDataSourceType.AutoDetect' or 'DynamicDataSourceType.Method' (auto detect is the default when not specified explicitly, and is recommended) + + '[DynamicData]' member '{0}.{1}' is a field so you should use 'DynamicDataSourceType.AutoDetect' or 'DynamicDataSourceType.Field' (auto detect is the default when not specified explicitly, and is recommended) + '[DynamicData]' display name method '{0}.{1}' signature is invalid @@ -534,6 +543,12 @@ The type declaring these methods should also respect the following rules: Use 'Assert.{0}' instead of 'Assert.{1}' + + Use 'Assert' instead of 'StringAssert' + + + Use 'Assert.{0}' instead of 'StringAssert.{1}' + The type of the generic parameter '{0}' could not be inferred. @@ -543,6 +558,9 @@ The type declaring these methods should also respect the following rules: '[DynamicData]' member '{0}.{1}' is not a property nor a method. Only properties and methods are supported. + + '[DynamicData]' member '{0}.{1}' is not a property, method, or field. Only properties, methods, and fields are supported. + Use newer methods to assert exceptions @@ -585,4 +603,103 @@ The type declaring these methods should also respect the following rules: An attribute that derives from 'RetryBaseAttribute' can be specified only on a test method - \ No newline at end of file + + Prefer 'TestMethod' over 'DataTestMethod' + + + 'DataTestMethod' is obsolete. Use 'TestMethod' instead. + + + 'DataTestMethodAttribute' is obsolete and provides no additional functionality over 'TestMethodAttribute'. Use 'TestMethodAttribute' for all test methods, including parameterized tests. + + + Use 'CooperativeCancellation = true' with '[Timeout]' + + + Use 'CooperativeCancellation = true' with '[Timeout]' to enable cooperative cancellation behavior + + + Using '[Timeout]' without explicitly setting 'CooperativeCancellation = true' is discouraged. In a future version, cooperative cancellation will become the default behavior. Set 'CooperativeCancellation = true' to opt into the recommended behavior and avoid breaking changes. + + + TestContext property cannot be accessed in this context + + + TestContext property '{0}' cannot be accessed in '{1}' method + + + Some TestContext properties are only available during test execution and cannot be accessed in assembly initialize, class initialize, class cleanup, or assembly cleanup methods. + + + Flow TestContext.CancellationToken to async operations + + + Consider using the overload that accepts a CancellationToken and pass 'TestContext.CancellationToken' + + + When calling async methods that have overloads accepting a CancellationToken parameter, prefer using the overload with TestContext.CancellationToken to enable cooperative cancellation and respect test timeouts. + + + Assert.Throws should contain only a single statement/expression + + + Assert.Throws should contain only a single statement/expression + + + Assert.Throws methods should contain only a single statement or expression. Multiple statements can be misleading - if the first statement throws, subsequent statements are never executed; if it doesn't throw, it should be moved outside the Assert.Throws. + + + GlobalTestInitialize and GlobalTestCleanup methods should have valid layout + + + Global test fixture method '{0}' signature is invalid + + + Methods marked with '[GlobalTestInitialize]' or '[GlobalTestCleanup]' should follow the following layout to be valid: +-it can't be declared on a generic class +-it should be 'public' +-it should be 'static' +-it should not be 'async void' +-it should not be a special method (finalizer, operator...). +-it should not be generic +-it should take one parameter of type 'TestContext' +-return type should be 'void', 'Task' or 'ValueTask' + +The type declaring these methods should also respect the following rules: +-The type should be a class +-The class should be 'public' +-The class shouldn't be 'static' +-The class should be marked with '[TestClass]' (or a derived attribute) +-the class should not be generic. + + + Avoid passing an explicit 'DynamicDataSourceType' and use the default auto detect behavior + + + Remove the 'DynamicDataSourceType' argument to use the default auto detect behavior + + + Use TestContext.CancellationToken instead of TestContext.CancellationTokenSource.Token + + + Use 'TestContext.CancellationToken' instead of 'TestContext.CancellationTokenSource.Token' + + + TestContext.CancellationToken provides a more direct way to access the cancellation token compared to TestContext.CancellationTokenSource.Token. + + + Avoid using Assert methods with format parameters + + + Replace '{0}' with format parameters with string.Format or string interpolation + + + Do not ignore the return value of string methods + + + The return value of '{0}' should not be ignored + + + Methods like Contains, StartsWith, and EndsWith return boolean values that indicate whether the condition was met. Ignoring these return values is likely a mistake. + + diff --git a/src/Analyzers/MSTest.Analyzers/RoslynAnalyzerHelpers/ArrayBuilder.Enumerator.cs b/src/Analyzers/MSTest.Analyzers/RoslynAnalyzerHelpers/ArrayBuilder.Enumerator.cs index 2a2fd9a7e4..48340fbf6a 100644 --- a/src/Analyzers/MSTest.Analyzers/RoslynAnalyzerHelpers/ArrayBuilder.Enumerator.cs +++ b/src/Analyzers/MSTest.Analyzers/RoslynAnalyzerHelpers/ArrayBuilder.Enumerator.cs @@ -32,7 +32,7 @@ public readonly void Dispose() { } - readonly object? System.Collections.IEnumerator.Current => Current; + readonly object? IEnumerator.Current => Current; public void Reset() => _index = -1; } diff --git a/src/Analyzers/MSTest.Analyzers/RoslynAnalyzerHelpers/BoundedCacheWithFactory.cs b/src/Analyzers/MSTest.Analyzers/RoslynAnalyzerHelpers/BoundedCacheWithFactory.cs index 87250620aa..3a760e6bd9 100644 --- a/src/Analyzers/MSTest.Analyzers/RoslynAnalyzerHelpers/BoundedCacheWithFactory.cs +++ b/src/Analyzers/MSTest.Analyzers/RoslynAnalyzerHelpers/BoundedCacheWithFactory.cs @@ -4,7 +4,7 @@ namespace Analyzer.Utilities; /// /// Provides bounded cache for analyzers. -/// Acts as a good alternative to +/// Acts as a good alternative to /// when the cached value has a cyclic reference to the key preventing early garbage collection of entries. /// internal sealed class BoundedCacheWithFactory diff --git a/src/Analyzers/MSTest.Analyzers/RoslynAnalyzerHelpers/DiagnosticExtensions.cs b/src/Analyzers/MSTest.Analyzers/RoslynAnalyzerHelpers/DiagnosticExtensions.cs index 7316c3ebb5..dbed1d2ae6 100644 --- a/src/Analyzers/MSTest.Analyzers/RoslynAnalyzerHelpers/DiagnosticExtensions.cs +++ b/src/Analyzers/MSTest.Analyzers/RoslynAnalyzerHelpers/DiagnosticExtensions.cs @@ -169,7 +169,7 @@ public static void ReportNoLocationDiagnostic( { #pragma warning disable RS0030 // The symbol 'DiagnosticDescriptor.DiagnosticDescriptor.#ctor' is banned in this project: Use 'DiagnosticDescriptorHelper.Create' instead rule = new DiagnosticDescriptor(rule.Id, rule.Title, rule.MessageFormat, rule.Category, - effectiveSeverity.Value, rule.IsEnabledByDefault, rule.Description, rule.HelpLinkUri, rule.CustomTags.ToArray()); + effectiveSeverity.Value, rule.IsEnabledByDefault, rule.Description, rule.HelpLinkUri, [.. rule.CustomTags]); #pragma warning restore RS0030 } diff --git a/src/Analyzers/MSTest.Analyzers/RoslynAnalyzerHelpers/IOperationExtensions.cs b/src/Analyzers/MSTest.Analyzers/RoslynAnalyzerHelpers/IOperationExtensions.cs index b4f48fb09f..e3ec94bd4c 100644 --- a/src/Analyzers/MSTest.Analyzers/RoslynAnalyzerHelpers/IOperationExtensions.cs +++ b/src/Analyzers/MSTest.Analyzers/RoslynAnalyzerHelpers/IOperationExtensions.cs @@ -5,6 +5,7 @@ using Microsoft.CodeAnalysis.Operations; namespace MSTest.Analyzers.RoslynAnalyzerHelpers; + internal static class IOperationExtensions { public static ISymbol? GetReferencedMemberOrLocalOrParameter(this IOperation? operation) => operation switch diff --git a/src/Analyzers/MSTest.Analyzers/StringAssertToAssertAnalyzer.cs b/src/Analyzers/MSTest.Analyzers/StringAssertToAssertAnalyzer.cs new file mode 100644 index 0000000000..eb0f44ffaf --- /dev/null +++ b/src/Analyzers/MSTest.Analyzers/StringAssertToAssertAnalyzer.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.Collections.Immutable; + +using Analyzer.Utilities.Extensions; + +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Operations; + +using MSTest.Analyzers.Helpers; + +namespace MSTest.Analyzers; + +/// +/// MSTEST0046: Use 'Assert' instead of 'StringAssert'. +/// +/// +/// The analyzer captures StringAssert method calls and suggests using equivalent Assert methods: +/// +/// +/// +/// StringAssert.Contains(value, substring)Assert.Contains(substring, value) +/// +/// +/// +/// +/// StringAssert.StartsWith(value, substring)Assert.StartsWith(substring, value) +/// +/// +/// +/// +/// StringAssert.EndsWith(value, substring)Assert.EndsWith(substring, value) +/// +/// +/// +/// +/// StringAssert.Matches(value, pattern)Assert.Matches(pattern, value) +/// +/// +/// +/// +/// StringAssert.DoesNotMatch(value, pattern)Assert.DoesNotMatch(pattern, value) +/// +/// +/// +/// +[DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] +internal sealed class StringAssertToAssertAnalyzer : DiagnosticAnalyzer +{ + /// + /// Key to retrieve the proper assert method name from the properties bag. + /// + internal const string ProperAssertMethodNameKey = nameof(ProperAssertMethodNameKey); + + private static readonly LocalizableResourceString Title = new(nameof(Resources.StringAssertToAssertTitle), Resources.ResourceManager, typeof(Resources)); + private static readonly LocalizableResourceString MessageFormat = new(nameof(Resources.StringAssertToAssertMessageFormat), Resources.ResourceManager, typeof(Resources)); + + internal static readonly DiagnosticDescriptor Rule = DiagnosticDescriptorHelper.Create( + DiagnosticIds.StringAssertToAssertRuleId, + Title, + MessageFormat, + 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.MicrosoftVisualStudioTestToolsUnitTestingStringAssert, out INamedTypeSymbol? stringAssertTypeSymbol)) + { + return; + } + + context.RegisterOperationAction(context => AnalyzeInvocationOperation(context, stringAssertTypeSymbol), OperationKind.Invocation); + }); + } + + private static void AnalyzeInvocationOperation(OperationAnalysisContext context, INamedTypeSymbol stringAssertTypeSymbol) + { + var operation = (IInvocationOperation)context.Operation; + IMethodSymbol targetMethod = operation.TargetMethod; + + if (!SymbolEqualityComparer.Default.Equals(targetMethod.ContainingType, stringAssertTypeSymbol)) + { + return; + } + + // Map StringAssert methods to their equivalent Assert methods + string? assertMethodName = targetMethod.Name switch + { + "Contains" => "Contains", + "StartsWith" => "StartsWith", + "EndsWith" => "EndsWith", + "Matches" => "MatchesRegex", + "DoesNotMatch" => "DoesNotMatchRegex", + _ => null, + }; + + if (assertMethodName == null) + { + return; + } + + // StringAssert methods all have at least 2 arguments that need to be swapped + if (operation.Arguments.Length < 2) + { + return; + } + + ImmutableDictionary properties = ImmutableDictionary.Empty.Add(ProperAssertMethodNameKey, assertMethodName); + + context.ReportDiagnostic(context.Operation.CreateDiagnostic( + Rule, + properties: properties, + assertMethodName, + targetMethod.Name)); + } +} diff --git a/src/Analyzers/MSTest.Analyzers/TestClassShouldHaveTestMethodAnalyzer.cs b/src/Analyzers/MSTest.Analyzers/TestClassShouldHaveTestMethodAnalyzer.cs index c8f38fac34..5ba9dde1b6 100644 --- a/src/Analyzers/MSTest.Analyzers/TestClassShouldHaveTestMethodAnalyzer.cs +++ b/src/Analyzers/MSTest.Analyzers/TestClassShouldHaveTestMethodAnalyzer.cs @@ -48,15 +48,17 @@ public override void Initialize(AnalysisContext context) INamedTypeSymbol? testMethodAttributeSymbol = context.Compilation.GetTypeByMetadataName(WellKnownTypeNames.MicrosoftVisualStudioTestToolsUnitTestingTestMethodAttribute); INamedTypeSymbol? assemblyInitializationAttributeSymbol = context.Compilation.GetTypeByMetadataName(WellKnownTypeNames.MicrosoftVisualStudioTestToolsUnitTestingAssemblyInitializeAttribute); INamedTypeSymbol? assemblyCleanupAttributeSymbol = context.Compilation.GetTypeByMetadataName(WellKnownTypeNames.MicrosoftVisualStudioTestToolsUnitTestingAssemblyCleanupAttribute); + INamedTypeSymbol? globalTestInitializeAttributeSymbol = context.Compilation.GetTypeByMetadataName(WellKnownTypeNames.MicrosoftVisualStudioTestToolsUnitTestingGlobalTestInitializeAttribute); + INamedTypeSymbol? globalTestCleanupAttributeSymbol = context.Compilation.GetTypeByMetadataName(WellKnownTypeNames.MicrosoftVisualStudioTestToolsUnitTestingGlobalTestCleanupAttribute); context.RegisterSymbolAction( - context => AnalyzeSymbol(context, testClassAttributeSymbol, testMethodAttributeSymbol, assemblyInitializationAttributeSymbol, assemblyCleanupAttributeSymbol), + context => AnalyzeSymbol(context, testClassAttributeSymbol, testMethodAttributeSymbol, assemblyInitializationAttributeSymbol, assemblyCleanupAttributeSymbol, globalTestInitializeAttributeSymbol, globalTestCleanupAttributeSymbol), SymbolKind.NamedType); } }); } private static void AnalyzeSymbol(SymbolAnalysisContext context, INamedTypeSymbol testClassAttributeSymbol, INamedTypeSymbol? testMethodAttributeSymbol, - INamedTypeSymbol? assemblyInitializationAttributeSymbol, INamedTypeSymbol? assemblyCleanupAttributeSymbol) + INamedTypeSymbol? assemblyInitializationAttributeSymbol, INamedTypeSymbol? assemblyCleanupAttributeSymbol, INamedTypeSymbol? globalTestInitializeAttributeSymbol, INamedTypeSymbol? globalTestCleanupAttributeSymbol) { var classSymbol = (INamedTypeSymbol)context.Symbol; @@ -91,7 +93,9 @@ private static void AnalyzeSymbol(SymbolAnalysisContext context, INamedTypeSymbo } if (SymbolEqualityComparer.Default.Equals(attribute.AttributeClass, assemblyInitializationAttributeSymbol) - || SymbolEqualityComparer.Default.Equals(attribute.AttributeClass, assemblyCleanupAttributeSymbol)) + || SymbolEqualityComparer.Default.Equals(attribute.AttributeClass, assemblyCleanupAttributeSymbol) + || SymbolEqualityComparer.Default.Equals(attribute.AttributeClass, globalTestInitializeAttributeSymbol) + || SymbolEqualityComparer.Default.Equals(attribute.AttributeClass, globalTestCleanupAttributeSymbol)) { hasAssemblyAttribute = true; } diff --git a/src/Analyzers/MSTest.Analyzers/TestContextPropertyUsageAnalyzer.cs b/src/Analyzers/MSTest.Analyzers/TestContextPropertyUsageAnalyzer.cs new file mode 100644 index 0000000000..1cedf89c78 --- /dev/null +++ b/src/Analyzers/MSTest.Analyzers/TestContextPropertyUsageAnalyzer.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.Collections.Immutable; + +using Analyzer.Utilities.Extensions; + +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Operations; + +using MSTest.Analyzers.Helpers; + +namespace MSTest.Analyzers; + +/// +/// MSTEST0047: . +/// +[DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] +public sealed class TestContextPropertyUsageAnalyzer : DiagnosticAnalyzer +{ + private static readonly LocalizableResourceString Title = new(nameof(Resources.TestContextPropertyUsageTitle), Resources.ResourceManager, typeof(Resources)); + private static readonly LocalizableResourceString MessageFormat = new(nameof(Resources.TestContextPropertyUsageMessageFormat), Resources.ResourceManager, typeof(Resources)); + private static readonly LocalizableResourceString Description = new(nameof(Resources.TestContextPropertyUsageDescription), Resources.ResourceManager, typeof(Resources)); + + internal static readonly DiagnosticDescriptor Rule = DiagnosticDescriptorHelper.Create( + DiagnosticIds.TestContextPropertyUsageRuleId, + Title, + MessageFormat, + Description, + Category.Usage, + DiagnosticSeverity.Warning, + isEnabledByDefault: true); + + /// + public override ImmutableArray SupportedDiagnostics { get; } = ImmutableArray.Create(Rule); + + // Properties that cannot be accessed in assembly initialize, class initialize, class cleanup, or assembly cleanup + private static readonly ImmutableHashSet RestrictedInAllFixtureMethods = ImmutableHashSet.Create( + StringComparer.Ordinal, + "TestData", + "TestDisplayName", + "DataRow", + "DataConnection", + "TestName", + "ManagedMethod"); + + // Properties that cannot be accessed in assembly initialize or assembly cleanup + private static readonly ImmutableHashSet RestrictedInAssemblyMethods = ImmutableHashSet.Create( + StringComparer.Ordinal, + "FullyQualifiedTestClassName", + "ManagedType"); + + /// + public override void Initialize(AnalysisContext context) + { + context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); + context.EnableConcurrentExecution(); + + context.RegisterCompilationStartAction(context => + { + if (!context.Compilation.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftVisualStudioTestToolsUnitTestingTestContext, out INamedTypeSymbol? testContextSymbol) + || !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)) + { + return; + } + + context.RegisterOperationAction(context => AnalyzePropertyReference(context, testContextSymbol, assemblyInitializeAttributeSymbol, assemblyCleanupAttributeSymbol, classInitializeAttributeSymbol, classCleanupAttributeSymbol), OperationKind.PropertyReference); + }); + } + + private static void AnalyzePropertyReference( + OperationAnalysisContext context, + INamedTypeSymbol testContextSymbol, + INamedTypeSymbol assemblyInitializeAttributeSymbol, + INamedTypeSymbol assemblyCleanupAttributeSymbol, + INamedTypeSymbol classInitializeAttributeSymbol, + INamedTypeSymbol classCleanupAttributeSymbol) + { + var propertyReference = (IPropertyReferenceOperation)context.Operation; + + // Check if the property is a TestContext property + if (!SymbolEqualityComparer.Default.Equals(propertyReference.Property.ContainingType, testContextSymbol)) + { + return; + } + + string propertyName = propertyReference.Property.Name; + + // Check if we're in a forbidden context + if (context.ContainingSymbol is not IMethodSymbol containingMethod) + { + return; + } + + // Check for assembly initialize/cleanup methods + bool isAssemblyInitialize = containingMethod.HasAttribute(assemblyInitializeAttributeSymbol); + bool isAssemblyCleanup = containingMethod.HasAttribute(assemblyCleanupAttributeSymbol); + bool isClassInitialize = containingMethod.HasAttribute(classInitializeAttributeSymbol); + bool isClassCleanup = containingMethod.HasAttribute(classCleanupAttributeSymbol); + + bool isInAssemblyMethod = isAssemblyInitialize || isAssemblyCleanup; + bool isInFixtureMethod = isInAssemblyMethod || isClassInitialize || isClassCleanup; + + // Check if the property is restricted in the current context + if (isInFixtureMethod && RestrictedInAllFixtureMethods.Contains(propertyName)) + { + context.ReportDiagnostic(propertyReference.CreateDiagnostic(Rule, propertyName, GetMethodType(isAssemblyInitialize, isAssemblyCleanup, isClassInitialize, isClassCleanup))); + } + else if (isInAssemblyMethod && RestrictedInAssemblyMethods.Contains(propertyName)) + { + context.ReportDiagnostic(propertyReference.CreateDiagnostic(Rule, propertyName, GetMethodType(isAssemblyInitialize, isAssemblyCleanup, isClassInitialize, isClassCleanup))); + } + } + + private static string GetMethodType(bool isAssemblyInitialize, bool isAssemblyCleanup, bool isClassInitialize, bool isClassCleanup) + => (isAssemblyInitialize, isAssemblyCleanup, isClassInitialize, isClassCleanup) switch + { + (true, _, _, _) => "AssemblyInitialize", + (_, true, _, _) => "AssemblyCleanup", + (_, _, true, _) => "ClassInitialize", + (_, _, _, true) => "ClassCleanup", + _ => "unknown", + }; +} diff --git a/src/Analyzers/MSTest.Analyzers/TestContextShouldBeValidAnalyzer.cs b/src/Analyzers/MSTest.Analyzers/TestContextShouldBeValidAnalyzer.cs index 4297c944d7..6838d3bbc7 100644 --- a/src/Analyzers/MSTest.Analyzers/TestContextShouldBeValidAnalyzer.cs +++ b/src/Analyzers/MSTest.Analyzers/TestContextShouldBeValidAnalyzer.cs @@ -92,11 +92,26 @@ private static bool AssignsParameterToMember(IParameterSymbol parameter, ISymbol operation = expressionStatementOperation.Operation; } - return operation is ISimpleAssignmentOperation assignmentOperation && + if (operation is ISimpleAssignmentOperation assignmentOperation && assignmentOperation.Target is IMemberReferenceOperation targetMemberReference && - SymbolEqualityComparer.Default.Equals(targetMemberReference.Member, member) && - assignmentOperation.Value is IParameterReferenceOperation parameterReference && - SymbolEqualityComparer.Default.Equals(parameterReference.Parameter, parameter); + SymbolEqualityComparer.Default.Equals(targetMemberReference.Member, member)) + { + // Extract parameter reference from the value, unwrapping from coalesce operation if necessary + IOperation effectiveValue = assignmentOperation.Value; + if (effectiveValue is ICoalesceOperation coalesceOperation) + { + effectiveValue = coalesceOperation.Value; + } + + // Check if the effective value is a parameter reference to our target parameter + if (effectiveValue is IParameterReferenceOperation parameterReference && + SymbolEqualityComparer.Default.Equals(parameterReference.Parameter, parameter)) + { + return true; + } + } + + return false; } private static void CollectTestContextFieldsAssignedInConstructor( @@ -125,11 +140,21 @@ private static void CollectTestContextFieldsAssignedInConstructor( } if (operation is ISimpleAssignmentOperation assignmentOperation && - assignmentOperation.Target is IMemberReferenceOperation { Member: IFieldSymbol { } candidateField } && - assignmentOperation.Value is IParameterReferenceOperation parameterReference && - SymbolEqualityComparer.Default.Equals(parameterReference.Parameter, testContextParameter)) + assignmentOperation.Target is IMemberReferenceOperation { Member: IFieldSymbol { } candidateField }) { - fieldsAssignedInConstructor.Add(candidateField); + // Extract parameter reference from the value, unwrapping from coalesce operation if necessary + IOperation effectiveValue = assignmentOperation.Value; + if (effectiveValue is ICoalesceOperation coalesceOperation) + { + effectiveValue = coalesceOperation.Value; + } + + // Check if the effective value is a parameter reference to our target parameter + if (effectiveValue is IParameterReferenceOperation parameterReference && + SymbolEqualityComparer.Default.Equals(parameterReference.Parameter, testContextParameter)) + { + fieldsAssignedInConstructor.Add(candidateField); + } } } @@ -187,7 +212,7 @@ public override void Initialize(AnalysisContext context) return; } - fieldsAssignedInConstructor = new(); + fieldsAssignedInConstructor = []; context.RegisterOperationBlockAction(context => { diff --git a/src/Analyzers/MSTest.Analyzers/TypeContainingTestMethodShouldBeATestClassAnalyzer.cs b/src/Analyzers/MSTest.Analyzers/TypeContainingTestMethodShouldBeATestClassAnalyzer.cs index f5c4d19827..998f4f1ba5 100644 --- a/src/Analyzers/MSTest.Analyzers/TypeContainingTestMethodShouldBeATestClassAnalyzer.cs +++ b/src/Analyzers/MSTest.Analyzers/TypeContainingTestMethodShouldBeATestClassAnalyzer.cs @@ -56,7 +56,7 @@ public override void Initialize(AnalysisContext context) private static void AnalyzeSymbol(SymbolAnalysisContext context, INamedTypeSymbol testClassAttributeSymbol, INamedTypeSymbol testMethodAttributeSymbol) { var namedTypeSymbol = (INamedTypeSymbol)context.Symbol; - if (namedTypeSymbol.TypeKind != TypeKind.Class + if ((namedTypeSymbol.TypeKind != TypeKind.Class && namedTypeSymbol.TypeKind != TypeKind.Struct) || namedTypeSymbol.IsAbstract) { return; diff --git a/src/Analyzers/MSTest.Analyzers/UnusedParameterSuppressor.cs b/src/Analyzers/MSTest.Analyzers/UnusedParameterSuppressor.cs new file mode 100644 index 0000000000..3bc64d707f --- /dev/null +++ b/src/Analyzers/MSTest.Analyzers/UnusedParameterSuppressor.cs @@ -0,0 +1,71 @@ +// 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; + +/// +/// MSTEST0047: . +/// +[DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] +public sealed class UnusedParameterSuppressor : DiagnosticSuppressor +{ + // IDE0060: Remove unused parameter 'name' if it is not part of a shipped public API + // https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0060 + private const string SuppressedDiagnosticId = "IDE0060"; + + internal static readonly SuppressionDescriptor Rule = + new(DiagnosticIds.UnusedParameterSuppressorRuleId, SuppressedDiagnosticId, Resources.UnusedParameterSuppressorJustification); + + /// + 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.MicrosoftVisualStudioTestToolsUnitTestingClassInitializeAttribute, out INamedTypeSymbol? classInitializeAttributeSymbol) + || !context.Compilation.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftVisualStudioTestToolsUnitTestingTestContext, out INamedTypeSymbol? testContextSymbol)) + { + return; + } + + INamedTypeSymbol? globalTestInitializeAttributeSymbol = context.Compilation.GetTypeByMetadataName(WellKnownTypeNames.MicrosoftVisualStudioTestToolsUnitTestingGlobalTestInitializeAttribute); + INamedTypeSymbol? globalTestCleanupAttributeSymbol = context.Compilation.GetTypeByMetadataName(WellKnownTypeNames.MicrosoftVisualStudioTestToolsUnitTestingGlobalTestCleanupAttribute); + + foreach (Diagnostic diagnostic in context.ReportedDiagnostics) + { + // The diagnostic is reported on the parameter + 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 IParameterSymbol parameter + && SymbolEqualityComparer.Default.Equals(testContextSymbol, parameter.Type) + && parameter.ContainingSymbol is IMethodSymbol method + && method.GetAttributes().Any(attr => + SymbolEqualityComparer.Default.Equals(attr.AttributeClass, assemblyInitializeAttributeSymbol) || + SymbolEqualityComparer.Default.Equals(attr.AttributeClass, classInitializeAttributeSymbol) || + SymbolEqualityComparer.Default.Equals(attr.AttributeClass, globalTestInitializeAttributeSymbol) || + SymbolEqualityComparer.Default.Equals(attr.AttributeClass, globalTestCleanupAttributeSymbol))) + { + context.ReportSuppression(Suppression.Create(Rule, diagnostic)); + } + } + } +} diff --git a/src/Analyzers/MSTest.Analyzers/UseAsyncSuffixTestFixtureMethodSuppressor.cs b/src/Analyzers/MSTest.Analyzers/UseAsyncSuffixTestFixtureMethodSuppressor.cs index 9f7995e4cf..3cd37a4c22 100644 --- a/src/Analyzers/MSTest.Analyzers/UseAsyncSuffixTestFixtureMethodSuppressor.cs +++ b/src/Analyzers/MSTest.Analyzers/UseAsyncSuffixTestFixtureMethodSuppressor.cs @@ -41,6 +41,9 @@ public override void ReportSuppressions(SuppressionAnalysisContext context) return; } + INamedTypeSymbol? globalTestInitializeAttributeSymbol = context.Compilation.GetTypeByMetadataName(WellKnownTypeNames.MicrosoftVisualStudioTestToolsUnitTestingGlobalTestInitializeAttribute); + INamedTypeSymbol? globalTestCleanupAttributeSymbol = context.Compilation.GetTypeByMetadataName(WellKnownTypeNames.MicrosoftVisualStudioTestToolsUnitTestingGlobalTestCleanupAttribute); + foreach (Diagnostic diagnostic in context.ReportedDiagnostics) { // The diagnostic is reported on the test method @@ -61,7 +64,9 @@ public override void ReportSuppressions(SuppressionAnalysisContext context) || SymbolEqualityComparer.Default.Equals(attr.AttributeClass, classInitializeAttributeSymbol) || SymbolEqualityComparer.Default.Equals(attr.AttributeClass, classCleanupAttributeSymbol) || SymbolEqualityComparer.Default.Equals(attr.AttributeClass, testInitializeAttributeSymbol) - || SymbolEqualityComparer.Default.Equals(attr.AttributeClass, testCleanupAttributeSymbol))) + || SymbolEqualityComparer.Default.Equals(attr.AttributeClass, testCleanupAttributeSymbol) + || SymbolEqualityComparer.Default.Equals(attr.AttributeClass, globalTestInitializeAttributeSymbol) + || SymbolEqualityComparer.Default.Equals(attr.AttributeClass, globalTestCleanupAttributeSymbol))) { context.ReportSuppression(Suppression.Create(Rule, diagnostic)); } diff --git a/src/Analyzers/MSTest.Analyzers/UseAttributeOnTestMethodAnalyzer.cs b/src/Analyzers/MSTest.Analyzers/UseAttributeOnTestMethodAnalyzer.cs index 63c73fd004..f3f8b2a28f 100644 --- a/src/Analyzers/MSTest.Analyzers/UseAttributeOnTestMethodAnalyzer.cs +++ b/src/Analyzers/MSTest.Analyzers/UseAttributeOnTestMethodAnalyzer.cs @@ -127,13 +127,15 @@ public sealed class UseAttributeOnTestMethodAnalyzer : DiagnosticAnalyzer isEnabledByDefault: true); // IMPORTANT: Remember to add any new rule to the rule tuple. + // IMPORTANT: The order is important. For example, Owner, Priority, and Description are also TestProperty. We report the first violation and then bail-out. + // It may be a better idea to consolidate OwnerRule, PriorityRule, DescriptionRule, and TestPropertyRule into a single rule. private static readonly List<(string AttributeFullyQualifiedName, DiagnosticDescriptor Rule)> RuleTuples = [ (WellKnownTypeNames.MicrosoftVisualStudioTestToolsUnitTestingOwnerAttribute, OwnerRule), (WellKnownTypeNames.MicrosoftVisualStudioTestToolsUnitTestingPriorityAttribute, PriorityRule), + (WellKnownTypeNames.MicrosoftVisualStudioTestToolsUnitTestingDescriptionAttribute, DescriptionRule), (WellKnownTypeNames.MicrosoftVisualStudioTestToolsUnitTestingTestPropertyAttribute, TestPropertyRule), (WellKnownTypeNames.MicrosoftVisualStudioTestToolsUnitTestingWorkItemAttribute, WorkItemRule), - (WellKnownTypeNames.MicrosoftVisualStudioTestToolsUnitTestingDescriptionAttribute, DescriptionRule), (WellKnownTypeNames.MicrosoftVisualStudioTestToolsUnitTestingExpectedExceptionBaseAttribute, ExpectedExceptionRule), (WellKnownTypeNames.MicrosoftVisualStudioTestToolsUnitTestingCssIterationAttribute, CssIterationRule), (WellKnownTypeNames.MicrosoftVisualStudioTestToolsUnitTestingCssProjectStructureAttribute, CssProjectStructureRule), @@ -214,6 +216,7 @@ private static void AnalyzeSymbol( if (methodAttribute.AttributeClass.Inherits(attributeSymbol)) { attributes.Add((methodAttribute, rule)); + break; } } } diff --git a/src/Analyzers/MSTest.Analyzers/UseCancellationTokenPropertyAnalyzer.cs b/src/Analyzers/MSTest.Analyzers/UseCancellationTokenPropertyAnalyzer.cs new file mode 100644 index 0000000000..5e58cae96b --- /dev/null +++ b/src/Analyzers/MSTest.Analyzers/UseCancellationTokenPropertyAnalyzer.cs @@ -0,0 +1,63 @@ +// 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; + +/// +/// MSTEST0054: . +/// +[DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] +public sealed class UseCancellationTokenPropertyAnalyzer : DiagnosticAnalyzer +{ + private static readonly LocalizableResourceString Title = new(nameof(Resources.UseCancellationTokenPropertyTitle), Resources.ResourceManager, typeof(Resources)); + private static readonly LocalizableResourceString Description = new(nameof(Resources.UseCancellationTokenPropertyDescription), Resources.ResourceManager, typeof(Resources)); + private static readonly LocalizableResourceString MessageFormat = new(nameof(Resources.UseCancellationTokenPropertyMessageFormat), Resources.ResourceManager, typeof(Resources)); + + internal static readonly DiagnosticDescriptor UseCancellationTokenPropertyRule = DiagnosticDescriptorHelper.Create( + DiagnosticIds.UseCancellationTokenPropertyRuleId, + Title, + MessageFormat, + Description, + Category.Usage, + DiagnosticSeverity.Info, + isEnabledByDefault: true); + + /// + public override ImmutableArray SupportedDiagnostics { get; } + = ImmutableArray.Create(UseCancellationTokenPropertyRule); + + /// + public override void Initialize(AnalysisContext context) + { + context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); + context.EnableConcurrentExecution(); + + context.RegisterCompilationStartAction(context => + { + if (context.Compilation.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftVisualStudioTestToolsUnitTestingTestContext, out INamedTypeSymbol? testContextSymbol) && + testContextSymbol.GetMembers("CancellationTokenSource") is [IPropertySymbol cancellationTokenSourcePropertySymbol]) + { + context.RegisterOperationAction(context => AnalyzePropertyReference(context, cancellationTokenSourcePropertySymbol), OperationKind.PropertyReference); + } + }); + } + + private static void AnalyzePropertyReference(OperationAnalysisContext context, IPropertySymbol cancellationTokenSourcePropertySymbol) + { + var propertyReferenceOperation = (IPropertyReferenceOperation)context.Operation; + if (propertyReferenceOperation.Property.Equals(cancellationTokenSourcePropertySymbol, SymbolEqualityComparer.Default)) + { + context.ReportDiagnostic(propertyReferenceOperation.Syntax.CreateDiagnostic(UseCancellationTokenPropertyRule)); + } + } +} diff --git a/src/Analyzers/MSTest.Analyzers/UseCooperativeCancellationForTimeoutAnalyzer.cs b/src/Analyzers/MSTest.Analyzers/UseCooperativeCancellationForTimeoutAnalyzer.cs new file mode 100644 index 0000000000..d73e67d3c0 --- /dev/null +++ b/src/Analyzers/MSTest.Analyzers/UseCooperativeCancellationForTimeoutAnalyzer.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; + +/// +/// MSTEST0045: . +/// +[DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] +public sealed class UseCooperativeCancellationForTimeoutAnalyzer : DiagnosticAnalyzer +{ + private static readonly LocalizableResourceString Title = new(nameof(Resources.UseCooperativeCancellationForTimeoutTitle), Resources.ResourceManager, typeof(Resources)); + private static readonly LocalizableResourceString Description = new(nameof(Resources.UseCooperativeCancellationForTimeoutDescription), Resources.ResourceManager, typeof(Resources)); + private static readonly LocalizableResourceString MessageFormat = new(nameof(Resources.UseCooperativeCancellationForTimeoutMessageFormat), Resources.ResourceManager, typeof(Resources)); + + internal static readonly DiagnosticDescriptor UseCooperativeCancellationForTimeoutRule = DiagnosticDescriptorHelper.Create( + DiagnosticIds.UseCooperativeCancellationForTimeoutRuleId, + Title, + MessageFormat, + Description, + Category.Design, + DiagnosticSeverity.Info, + isEnabledByDefault: true); + + /// + public override ImmutableArray SupportedDiagnostics { get; } + = ImmutableArray.Create(UseCooperativeCancellationForTimeoutRule); + + /// + public override void Initialize(AnalysisContext context) + { + context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); + context.EnableConcurrentExecution(); + + context.RegisterCompilationStartAction(context => + { + if (context.Compilation.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftVisualStudioTestToolsUnitTestingTimeoutAttribute, out INamedTypeSymbol? timeoutAttributeSymbol)) + { + context.RegisterSymbolAction( + context => AnalyzeSymbol(context, timeoutAttributeSymbol), + SymbolKind.Method); + } + }); + } + + private static void AnalyzeSymbol(SymbolAnalysisContext context, INamedTypeSymbol timeoutAttributeSymbol) + { + var methodSymbol = (IMethodSymbol)context.Symbol; + + AttributeData? timeoutAttribute = null; + foreach (AttributeData attribute in methodSymbol.GetAttributes()) + { + if (SymbolEqualityComparer.Default.Equals(attribute.AttributeClass, timeoutAttributeSymbol)) + { + timeoutAttribute = attribute; + break; + } + } + + // Report diagnostic if CooperativeCancellation is not explicitly set to true + if (timeoutAttribute is not null + && !timeoutAttribute.NamedArguments.Any(x => x.Key == "CooperativeCancellation" && x.Value.Value is bool boolValue && boolValue)) + { + if (timeoutAttribute.ApplicationSyntaxReference?.GetSyntax() is { } syntax) + { + context.ReportDiagnostic(syntax.CreateDiagnostic(UseCooperativeCancellationForTimeoutRule)); + } + } + } +} diff --git a/src/Analyzers/MSTest.Analyzers/UseNewerAssertThrowsAnalyzer.cs b/src/Analyzers/MSTest.Analyzers/UseNewerAssertThrowsAnalyzer.cs index d28aa7890f..0888a9d72d 100644 --- a/src/Analyzers/MSTest.Analyzers/UseNewerAssertThrowsAnalyzer.cs +++ b/src/Analyzers/MSTest.Analyzers/UseNewerAssertThrowsAnalyzer.cs @@ -28,7 +28,7 @@ internal sealed class UseNewerAssertThrowsAnalyzer : DiagnosticAnalyzer MessageFormat, null, Category.Usage, - DiagnosticSeverity.Info, + DiagnosticSeverity.Warning, isEnabledByDefault: true); public override ImmutableArray SupportedDiagnostics { get; } diff --git a/src/Analyzers/MSTest.Analyzers/UseParallelizeAttributeAnalyzer.cs b/src/Analyzers/MSTest.Analyzers/UseParallelizeAttributeAnalyzer.cs index 3ab2f55f99..7afe6c69fc 100644 --- a/src/Analyzers/MSTest.Analyzers/UseParallelizeAttributeAnalyzer.cs +++ b/src/Analyzers/MSTest.Analyzers/UseParallelizeAttributeAnalyzer.cs @@ -45,7 +45,10 @@ public override void Initialize(AnalysisContext context) private static void AnalyzeCompilation(CompilationAnalysisContext context) { - bool hasTestAdapter = context.Compilation.ReferencedAssemblyNames.Any(asm => asm.Name == "Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter"); + bool hasTestAdapter = context.Options.AnalyzerConfigOptionsProvider.GlobalOptions.TryGetValue("build_property.IsMSTestTestAdapterReferenced", out string? isAdapterReferenced) && + bool.TryParse(isAdapterReferenced, out bool isAdapterReferencedValue) && + isAdapterReferencedValue; + if (!hasTestAdapter) { // We shouldn't produce a diagnostic if only the test framework is referenced, but not the adapter. diff --git a/src/Analyzers/MSTest.Analyzers/UseProperAssertMethodsAnalyzer.cs b/src/Analyzers/MSTest.Analyzers/UseProperAssertMethodsAnalyzer.cs index ef26733c67..47141d2c2e 100644 --- a/src/Analyzers/MSTest.Analyzers/UseProperAssertMethodsAnalyzer.cs +++ b/src/Analyzers/MSTest.Analyzers/UseProperAssertMethodsAnalyzer.cs @@ -41,6 +41,41 @@ namespace MSTest.Analyzers; /// Assert.[AreEqual|AreNotEqual](null, x) /// /// +/// +/// +/// Assert.IsTrue(myString.[StartsWith|EndsWith|Contains]("...")) +/// +/// +/// +/// +/// Assert.IsFalse(myString.[StartsWith|EndsWith|Contains]("...")) +/// +/// +/// +/// +/// Assert.IsTrue(myCollection.Contains(...)) +/// +/// +/// +/// +/// Assert.IsFalse(myCollection.Contains(...)) +/// +/// +/// +/// +/// Assert.[IsTrue|IsFalse](x [>|>=|<|<=] y) +/// +/// +/// +/// +/// Assert.AreEqual([0|X], myCollection.[Count|Length]) +/// +/// +/// +/// +/// Assert.IsTrue(myCollection.[Count|Length] [>|!=|==] 0) +/// +/// /// /// [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] @@ -60,6 +95,36 @@ private enum EqualityCheckStatus NotEquals, } + private enum StringMethodCheckStatus + { + Unknown, + StartsWith, + EndsWith, + Contains, + } + + private enum ComparisonCheckStatus + { + Unknown, + GreaterThan, + GreaterThanOrEqual, + LessThan, + LessThanOrEqual, + } + + private enum CollectionCheckStatus + { + Unknown, + Contains, + } + + private enum CountCheckStatus + { + Unknown, + IsEmpty, + HasCount, + } + internal const string ProperAssertMethodNameKey = nameof(ProperAssertMethodNameKey); /// @@ -84,6 +149,7 @@ private enum EqualityCheckStatus /// The value for ProperAssertMethodNameKey is "IsNull". /// The first additional location will point to the "x == null" node. /// The second additional location will point to the "x" node. + /// Optionally, more additional locations will also be interpreted as "replace" operations. /// internal const string CodeFixModeSimple = nameof(CodeFixModeSimple); @@ -108,7 +174,7 @@ private enum EqualityCheckStatus /// /// Find the right assert method name from the properties bag using . /// Replace the identifier syntax for the invocation with the right assert method name. The identifier syntax is calculated by the codefix. - /// Remove the argument which the second additional locations points to. + /// Remove the argument which the first additional location points to. /// /// Example: For Assert.AreEqual(false, x), it will become Assert.IsFalse(x). /// The value for ProperAssertMethodNameKey is "IsFalse". @@ -120,6 +186,22 @@ private enum EqualityCheckStatus /// internal const string CodeFixModeRemoveArgument = nameof(CodeFixModeRemoveArgument); + /// + /// This mode means the codefix operation is as follows: + /// + /// Find the right assert method name from the properties bag using . + /// Replace the identifier syntax for the invocation with the right assert method name. The identifier syntax is calculated by the codefix. + /// Remove the argument which the first additional location points to. + /// Replace the argument which the second additional location points to with the expression pointed to by the third additional location + /// + /// Example: For Assert.AreEqual(0, list.Count), it will become Assert.IsEmpty(list). + /// The value for ProperAssertMethodNameKey is "IsEmpty". + /// The first additional location will point to the "0" node. + /// The second additional location will point to the "list.Count" node. + /// The third additional location will point to the "list" node. + /// + internal const string CodeFixModeRemoveArgumentAndReplaceArgument = nameof(CodeFixModeRemoveArgumentAndReplaceArgument); + private static readonly LocalizableResourceString Title = new(nameof(Resources.UseProperAssertMethodsTitle), Resources.ResourceManager, typeof(Resources)); private static readonly LocalizableResourceString MessageFormat = new(nameof(Resources.UseProperAssertMethodsMessageFormat), Resources.ResourceManager, typeof(Resources)); @@ -147,11 +229,13 @@ public override void Initialize(AnalysisContext context) return; } - context.RegisterOperationAction(context => AnalyzeInvocationOperation(context, assertTypeSymbol), OperationKind.Invocation); + INamedTypeSymbol objectTypeSymbol = context.Compilation.GetSpecialType(SpecialType.System_Object); + + context.RegisterOperationAction(context => AnalyzeInvocationOperation(context, assertTypeSymbol, objectTypeSymbol), OperationKind.Invocation); }); } - private static void AnalyzeInvocationOperation(OperationAnalysisContext context, INamedTypeSymbol assertTypeSymbol) + private static void AnalyzeInvocationOperation(OperationAnalysisContext context, INamedTypeSymbol assertTypeSymbol, INamedTypeSymbol objectTypeSymbol) { var operation = (IInvocationOperation)context.Operation; IMethodSymbol targetMethod = operation.TargetMethod; @@ -168,19 +252,19 @@ private static void AnalyzeInvocationOperation(OperationAnalysisContext context, switch (targetMethod.Name) { case "IsTrue": - AnalyzeIsTrueOrIsFalseInvocation(context, firstArgument, isTrueInvocation: true); + AnalyzeIsTrueOrIsFalseInvocation(context, firstArgument, isTrueInvocation: true, objectTypeSymbol); break; case "IsFalse": - AnalyzeIsTrueOrIsFalseInvocation(context, firstArgument, isTrueInvocation: false); + AnalyzeIsTrueOrIsFalseInvocation(context, firstArgument, isTrueInvocation: false, objectTypeSymbol); break; case "AreEqual": - AnalyzeAreEqualOrAreNotEqualInvocation(context, firstArgument, isAreEqualInvocation: true); + AnalyzeAreEqualOrAreNotEqualInvocation(context, firstArgument, isAreEqualInvocation: true, objectTypeSymbol); break; case "AreNotEqual": - AnalyzeAreEqualOrAreNotEqualInvocation(context, firstArgument, isAreEqualInvocation: false); + AnalyzeAreEqualOrAreNotEqualInvocation(context, firstArgument, isAreEqualInvocation: false, objectTypeSymbol); break; } } @@ -325,7 +409,115 @@ private static bool CanUseTypeAsObject(Compilation compilation, ITypeSymbol? typ => type is null || compilation.ClassifyCommonConversion(type, compilation.GetSpecialType(SpecialType.System_Object)).Exists; - private static void AnalyzeIsTrueOrIsFalseInvocation(OperationAnalysisContext context, IOperation conditionArgument, bool isTrueInvocation) + private static StringMethodCheckStatus RecognizeStringMethodCheck( + IOperation operation, + out SyntaxNode? stringExpression, + out SyntaxNode? substringExpression) + { + if (operation is IInvocationOperation invocation && + invocation.TargetMethod.ContainingType?.SpecialType == SpecialType.System_String && + invocation.Arguments.Length == 1) + { + string methodName = invocation.TargetMethod.Name; + if (methodName is "StartsWith" or "EndsWith" or "Contains") + { + stringExpression = invocation.Instance?.Syntax; + substringExpression = invocation.Arguments[0].Value.Syntax; + + return methodName switch + { + "StartsWith" => StringMethodCheckStatus.StartsWith, + "EndsWith" => StringMethodCheckStatus.EndsWith, + "Contains" => StringMethodCheckStatus.Contains, + _ => StringMethodCheckStatus.Unknown, + }; + } + } + + stringExpression = null; + substringExpression = null; + return StringMethodCheckStatus.Unknown; + } + + private static CollectionCheckStatus RecognizeCollectionMethodCheck( + IOperation operation, + INamedTypeSymbol objectTypeSymbol, + out SyntaxNode? collectionExpression, + out SyntaxNode? itemExpression) + { + if (operation is IInvocationOperation invocation) + { + string methodName = invocation.TargetMethod.Name; + + // Check for Collection.Contains(item) + // We need to also ensure that invocation.TargetMethod.OriginalDefinition.Parameters[0] matches the type of the ienumerable returned by invocation.TargetMethod.ContainingType.OriginalDefinition.AllInterfaces + if (methodName == "Contains" && + invocation.Arguments.Length == 1 && + invocation.TargetMethod.OriginalDefinition.Parameters.Length == 1 && + invocation.TargetMethod.OriginalDefinition.Parameters[0].Type is { } containsParameterType) + { + if (IsBCLCollectionType(invocation.TargetMethod.ContainingType, objectTypeSymbol)) + { + ITypeSymbol? enumerableElementType = invocation.TargetMethod.ContainingType.OriginalDefinition.AllInterfaces.FirstOrDefault( + i => i.OriginalDefinition.SpecialType == SpecialType.System_Collections_Generic_IEnumerable_T)?.TypeArguments[0]; + + if (enumerableElementType is null || enumerableElementType.Equals(containsParameterType, SymbolEqualityComparer.Default)) + { + // If enumerableElementType is null, we expect that this is a non-generic IEnumerable. So we simply report the diagnostic. + // If it's not null, ensure that the "T" of "IEnumerable" matches the type of the Contains method. + // The type comparison here is not for "substituted" types. + // So, when analyzing KeyedCollection.Contains(TKey), we will have: + // 1. containsParameterType as the symbol referring to "TKey". + // 2. enumerableElementType as the symbol referring to "TItem". + // So, even if we are dealing with KeyedCollection, the types won't match, and we won't produce a diagnostic. + collectionExpression = invocation.Instance?.Syntax; + itemExpression = invocation.Arguments[0].Value.Syntax; + return CollectionCheckStatus.Contains; + } + } + } + } + + collectionExpression = null; + itemExpression = null; + return CollectionCheckStatus.Unknown; + } + + private static bool IsBCLCollectionType(ITypeSymbol type, INamedTypeSymbol objectTypeSymbol) + // Check if the type implements IEnumerable (but is not string) + => type.SpecialType != SpecialType.System_String && type.AllInterfaces.Any(i => + i.OriginalDefinition.SpecialType == SpecialType.System_Collections_IEnumerable) && + // object is coming from BCL and it's expected to always have a public key. + type.ContainingAssembly.Identity.HasPublicKey == objectTypeSymbol.ContainingAssembly.Identity.HasPublicKey && + type.ContainingAssembly.Identity.PublicKey.SequenceEqual(objectTypeSymbol.ContainingAssembly.Identity.PublicKey); + + private static ComparisonCheckStatus RecognizeComparisonCheck( + IOperation operation, + out SyntaxNode? leftExpression, + out SyntaxNode? rightExpression) + { + if (operation is IBinaryOperation binaryOperation && + binaryOperation.OperatorMethod is not { MethodKind: MethodKind.UserDefinedOperator }) + { + leftExpression = binaryOperation.LeftOperand.Syntax; + rightExpression = binaryOperation.RightOperand.Syntax; + + return binaryOperation.OperatorKind switch + { + BinaryOperatorKind.GreaterThan => ComparisonCheckStatus.GreaterThan, + BinaryOperatorKind.GreaterThanOrEqual => ComparisonCheckStatus.GreaterThanOrEqual, + BinaryOperatorKind.LessThan => ComparisonCheckStatus.LessThan, + BinaryOperatorKind.LessThanOrEqual => ComparisonCheckStatus.LessThanOrEqual, + _ => ComparisonCheckStatus.Unknown, + }; + } + + leftExpression = null; + rightExpression = null; + return ComparisonCheckStatus.Unknown; + } + + private static void AnalyzeIsTrueOrIsFalseInvocation(OperationAnalysisContext context, IOperation conditionArgument, bool isTrueInvocation, INamedTypeSymbol objectTypeSymbol) { RoslynDebug.Assert(context.Operation is IInvocationOperation, "Expected IInvocationOperation."); @@ -361,6 +553,137 @@ private static void AnalyzeIsTrueOrIsFalseInvocation(OperationAnalysisContext co return; } + // Check for string method patterns: myString.StartsWith/EndsWith/Contains(...) + StringMethodCheckStatus stringMethodStatus = RecognizeStringMethodCheck(conditionArgument, out SyntaxNode? stringExpr, out SyntaxNode? substringExpr); + if (stringMethodStatus != StringMethodCheckStatus.Unknown) + { + // Handle both IsTrue and IsFalse cases with string methods + string properAssertMethod = stringMethodStatus switch + { + StringMethodCheckStatus.StartsWith => isTrueInvocation ? "StartsWith" : "DoesNotStartWith", + StringMethodCheckStatus.EndsWith => isTrueInvocation ? "EndsWith" : "DoesNotEndWith", + StringMethodCheckStatus.Contains => isTrueInvocation ? "Contains" : "DoesNotContain", + _ => throw new InvalidOperationException("Unexpected StringMethodCheckStatus value."), + }; + + ImmutableDictionary.Builder properties = ImmutableDictionary.CreateBuilder(); + properties.Add(ProperAssertMethodNameKey, properAssertMethod); + properties.Add(CodeFixModeKey, CodeFixModeAddArgument); + context.ReportDiagnostic(context.Operation.CreateDiagnostic( + Rule, + additionalLocations: ImmutableArray.Create(conditionArgument.Syntax.GetLocation(), substringExpr!.GetLocation(), stringExpr!.GetLocation()), + properties: properties.ToImmutable(), + properAssertMethod, + isTrueInvocation ? "IsTrue" : "IsFalse")); + return; + } + + // Check for collection method patterns: myCollection.Contains(...) + CollectionCheckStatus collectionMethodStatus = RecognizeCollectionMethodCheck(conditionArgument, objectTypeSymbol, out SyntaxNode? collectionExpr, out SyntaxNode? itemExpr); + if (collectionMethodStatus != CollectionCheckStatus.Unknown) + { + if (collectionMethodStatus == CollectionCheckStatus.Contains) + { + string properAssertMethod = isTrueInvocation ? "Contains" : "DoesNotContain"; + + ImmutableDictionary.Builder properties = ImmutableDictionary.CreateBuilder(); + properties.Add(ProperAssertMethodNameKey, properAssertMethod); + properties.Add(CodeFixModeKey, CodeFixModeAddArgument); + context.ReportDiagnostic(context.Operation.CreateDiagnostic( + Rule, + additionalLocations: ImmutableArray.Create(conditionArgument.Syntax.GetLocation(), itemExpr!.GetLocation(), collectionExpr!.GetLocation()), + properties: properties.ToImmutable(), + properAssertMethod, + isTrueInvocation ? "IsTrue" : "IsFalse")); + return; + } + } + + // Check for collection emptiness patterns: myCollection.Count > 0, myCollection.Count != 0, or myCollection.Count == 0 + CountCheckStatus countStatus = RecognizeCountCheck(conditionArgument, objectTypeSymbol, out SyntaxNode? collectionEmptinessExpr); + if (countStatus != CountCheckStatus.Unknown) + { + string properAssertMethod = countStatus switch + { + CountCheckStatus.IsEmpty => isTrueInvocation ? "IsEmpty" : "IsNotEmpty", + CountCheckStatus.HasCount => isTrueInvocation ? "IsNotEmpty" : "IsEmpty", + _ => throw ApplicationStateGuard.Unreachable(), + }; + + ImmutableDictionary.Builder properties = ImmutableDictionary.CreateBuilder(); + properties.Add(ProperAssertMethodNameKey, properAssertMethod); + properties.Add(CodeFixModeKey, CodeFixModeSimple); + context.ReportDiagnostic(context.Operation.CreateDiagnostic( + Rule, + additionalLocations: ImmutableArray.Create(conditionArgument.Syntax.GetLocation(), collectionEmptinessExpr!.GetLocation()), + properties: properties.ToImmutable(), + properAssertMethod, + isTrueInvocation ? "IsTrue" : "IsFalse")); + return; + } + + // Check for comparison patterns: a > b, a >= b, a < b, a <= b + ComparisonCheckStatus comparisonStatus = RecognizeComparisonCheck(conditionArgument, out SyntaxNode? leftExpr, out SyntaxNode? rightExpr); + if (comparisonStatus != ComparisonCheckStatus.Unknown) + { + string properAssertMethod = (isTrueInvocation, comparisonStatus) switch + { + (true, ComparisonCheckStatus.GreaterThan) => "IsGreaterThan", + (true, ComparisonCheckStatus.GreaterThanOrEqual) => "IsGreaterThanOrEqualTo", + (true, ComparisonCheckStatus.LessThan) => "IsLessThan", + (true, ComparisonCheckStatus.LessThanOrEqual) => "IsLessThanOrEqualTo", + (false, ComparisonCheckStatus.GreaterThan) => "IsLessThanOrEqualTo", + (false, ComparisonCheckStatus.GreaterThanOrEqual) => "IsLessThan", + (false, ComparisonCheckStatus.LessThan) => "IsGreaterThanOrEqualTo", + (false, ComparisonCheckStatus.LessThanOrEqual) => "IsGreaterThan", + _ => throw new InvalidOperationException("Unexpected ComparisonCheckStatus value."), + }; + + // For Assert.IsGreaterThan, IsLessThan etc., the method signature is (lowerBound, value) or (upperBound, value) + // So for a > b -> Assert.IsGreaterThan(b, a) where b is the lower bound and a is the value + // For a < b -> Assert.IsLessThan(b, a) where b is the upper bound and a is the value + SyntaxNode? firstArg, secondArg; + switch ((isTrueInvocation, comparisonStatus)) + { + // a > b -> IsGreaterThan(b, a) + case (true, ComparisonCheckStatus.GreaterThan): + // a >= b -> IsGreaterThanOrEqualTo(b, a) + case (true, ComparisonCheckStatus.GreaterThanOrEqual): + // !(a < b) -> IsGreaterThanOrEqualTo(b, a) + case (false, ComparisonCheckStatus.LessThan): + // !(a <= b) -> IsGreaterThan(b, a) + case (false, ComparisonCheckStatus.LessThanOrEqual): + firstArg = rightExpr; // b becomes first arg (lower bound) + secondArg = leftExpr; // a becomes second arg (value) + break; + // a < b -> IsLessThan(b, a) + case (true, ComparisonCheckStatus.LessThan): + // a <= b -> IsLessThanOrEqualTo(b, a) + case (true, ComparisonCheckStatus.LessThanOrEqual): + // !(a > b) -> IsLessThanOrEqualTo(b, a) + case (false, ComparisonCheckStatus.GreaterThan): + // !(a >= b) -> IsLessThan(b, a) + case (false, ComparisonCheckStatus.GreaterThanOrEqual): + firstArg = rightExpr; // b becomes first arg (upper bound) + secondArg = leftExpr; // a becomes second arg (value) + break; + + default: + throw new InvalidOperationException("Unexpected comparison case."); + } + + ImmutableDictionary.Builder properties = ImmutableDictionary.CreateBuilder(); + properties.Add(ProperAssertMethodNameKey, properAssertMethod); + properties.Add(CodeFixModeKey, CodeFixModeAddArgument); + context.ReportDiagnostic(context.Operation.CreateDiagnostic( + Rule, + additionalLocations: ImmutableArray.Create(conditionArgument.Syntax.GetLocation(), firstArg!.GetLocation(), secondArg!.GetLocation()), + properties: properties.ToImmutable(), + properAssertMethod, + isTrueInvocation ? "IsTrue" : "IsFalse")); + return; + } + EqualityCheckStatus equalityCheckStatus = RecognizeEqualityCheck(conditionArgument, out SyntaxNode? toBecomeExpected, out SyntaxNode? toBecomeActual, out ITypeSymbol? leftType, out ITypeSymbol? rightType); if (equalityCheckStatus != EqualityCheckStatus.Unknown && CanUseTypeAsObject(context.Compilation, leftType) && @@ -390,8 +713,68 @@ private static void AnalyzeIsTrueOrIsFalseInvocation(OperationAnalysisContext co } } - private static void AnalyzeAreEqualOrAreNotEqualInvocation(OperationAnalysisContext context, IOperation expectedArgument, bool isAreEqualInvocation) + private static void AnalyzeAreEqualOrAreNotEqualInvocation(OperationAnalysisContext context, IOperation expectedArgument, bool isAreEqualInvocation, INamedTypeSymbol objectTypeSymbol) { + // Check for collection count patterns: collection.Count/Length == 0 or collection.Count/Length == X + if (isAreEqualInvocation) + { + if (TryGetSecondArgumentValue((IInvocationOperation)context.Operation, out IOperation? actualArgumentValue)) + { + // Check if we're comparing a count/length property + CountCheckStatus countStatus = RecognizeCountCheck( + expectedArgument, + actualArgumentValue, + objectTypeSymbol, + out SyntaxNode? nodeToBeReplaced1, + out SyntaxNode? replacement1, + out SyntaxNode? nodeToBeReplaced2, + out SyntaxNode? replacement2); + + if (countStatus != CountCheckStatus.Unknown) + { + if (nodeToBeReplaced1 is null || replacement1 is null) + { + throw ApplicationStateGuard.Unreachable(); + } + + string properAssertMethod = countStatus == CountCheckStatus.IsEmpty ? "IsEmpty" : "HasCount"; + + ImmutableDictionary.Builder properties = ImmutableDictionary.CreateBuilder(); + properties.Add(ProperAssertMethodNameKey, properAssertMethod); + + if (nodeToBeReplaced2 is not null && replacement2 is null) + { + // Here we suggest Assert.IsEmpty(collection) + properties.Add(CodeFixModeKey, CodeFixModeRemoveArgumentAndReplaceArgument); + context.ReportDiagnostic(context.Operation.CreateDiagnostic( + Rule, + additionalLocations: ImmutableArray.Create( + nodeToBeReplaced2.GetLocation(), + nodeToBeReplaced1.GetLocation(), + replacement1.GetLocation()), + properties: properties.ToImmutable(), + properAssertMethod, + "AreEqual")); + } + else + { + // Here we suggest Assert.HasCount(expectedCount, collection) + properties.Add(CodeFixModeKey, CodeFixModeSimple); + context.ReportDiagnostic(context.Operation.CreateDiagnostic( + Rule, + additionalLocations: nodeToBeReplaced2 is not null && replacement2 is not null + ? ImmutableArray.Create(nodeToBeReplaced1.GetLocation(), replacement1.GetLocation(), nodeToBeReplaced2.GetLocation(), replacement2.GetLocation()) + : ImmutableArray.Create(nodeToBeReplaced1.GetLocation(), replacement1.GetLocation()), + properties: properties.ToImmutable(), + properAssertMethod, + "AreEqual")); + } + + return; + } + } + } + // Don't flag a warning for Assert.AreNotEqual([true|false], x). // This is not the same as Assert.IsFalse(x). if (isAreEqualInvocation && expectedArgument is ILiteralOperation { ConstantValue: { HasValue: true, Value: bool expectedLiteralBoolean } }) @@ -446,6 +829,119 @@ actualArgumentValue.Type is { } actualType && } } + private static CountCheckStatus RecognizeCountCheck( + IOperation operation, + INamedTypeSymbol objectTypeSymbol, + out SyntaxNode? collectionExpression) + { + collectionExpression = null; + + // Check for collection.Count > 0 or collection.Length > 0 + if (operation is IBinaryOperation { OperatorKind: BinaryOperatorKind.GreaterThan, LeftOperand: IPropertyReferenceOperation propertyRef, RightOperand: ILiteralOperation { ConstantValue: { HasValue: true, Value: 0 } } } && + TryGetCollectionExpressionIfBCLCollectionLengthOrCount(propertyRef, objectTypeSymbol) is { } expression) + { + collectionExpression = expression; + return CountCheckStatus.HasCount; + } + + // Check for 0 < collection.Count or 0 < collection.Length + if (operation is IBinaryOperation { OperatorKind: BinaryOperatorKind.LessThan, LeftOperand: ILiteralOperation { ConstantValue: { HasValue: true, Value: 0 } }, RightOperand: IPropertyReferenceOperation propertyRef2 } && + TryGetCollectionExpressionIfBCLCollectionLengthOrCount(propertyRef2, objectTypeSymbol) is { } expression2) + { + collectionExpression = expression2; + return CountCheckStatus.HasCount; + } + + // Check for collection.Count != 0 or collection.Length != 0 + if (operation is IBinaryOperation { OperatorKind: BinaryOperatorKind.NotEquals, LeftOperand: IPropertyReferenceOperation propertyRef3, RightOperand: ILiteralOperation { ConstantValue: { HasValue: true, Value: 0 } } } && + TryGetCollectionExpressionIfBCLCollectionLengthOrCount(propertyRef3, objectTypeSymbol) is { } expression3) + { + collectionExpression = expression3; + return CountCheckStatus.HasCount; + } + + // Check for 0 != collection.Count or 0 != collection.Length (reverse order) + if (operation is IBinaryOperation { OperatorKind: BinaryOperatorKind.NotEquals, LeftOperand: ILiteralOperation { ConstantValue: { HasValue: true, Value: 0 } }, RightOperand: IPropertyReferenceOperation propertyRef4 } && + TryGetCollectionExpressionIfBCLCollectionLengthOrCount(propertyRef4, objectTypeSymbol) is { } expression4) + { + collectionExpression = expression4; + return CountCheckStatus.HasCount; + } + + // Check for collection.Count == 0 or collection.Length == 0 + if (operation is IBinaryOperation { OperatorKind: BinaryOperatorKind.Equals, LeftOperand: IPropertyReferenceOperation propertyRef5, RightOperand: ILiteralOperation { ConstantValue: { HasValue: true, Value: 0 } } } && + TryGetCollectionExpressionIfBCLCollectionLengthOrCount(propertyRef5, objectTypeSymbol) is { } expression5) + { + collectionExpression = expression5; + return CountCheckStatus.IsEmpty; + } + + // Check for 0 == collection.Count or 0 == collection.Length (reverse order) + if (operation is IBinaryOperation { OperatorKind: BinaryOperatorKind.Equals, LeftOperand: ILiteralOperation { ConstantValue: { HasValue: true, Value: 0 } }, RightOperand: IPropertyReferenceOperation propertyRef6 } && + TryGetCollectionExpressionIfBCLCollectionLengthOrCount(propertyRef6, objectTypeSymbol) is { } expression6) + { + collectionExpression = expression6; + return CountCheckStatus.IsEmpty; + } + + return CountCheckStatus.Unknown; + } + + private static CountCheckStatus RecognizeCountCheck( + IOperation expectedArgument, + IOperation actualArgument, + INamedTypeSymbol objectTypeSymbol, + out SyntaxNode? nodeToBeReplaced1, + out SyntaxNode? replacement1, + out SyntaxNode? nodeToBeReplaced2, + out SyntaxNode? replacement2) + { + // Check if actualArgument is a count/length property + if (actualArgument is IPropertyReferenceOperation propertyRef && + TryGetCollectionExpressionIfBCLCollectionLengthOrCount(propertyRef, objectTypeSymbol) is { } expression) + { + bool isEmpty = expectedArgument.ConstantValue.HasValue && + expectedArgument.ConstantValue.Value is int expectedValue && + expectedValue == 0; + + if (isEmpty) + { + // We have Assert.AreEqual(0, collection.Count/Length) + // We want Assert.IsEmpty(collection) + // So, only a single replacement is needed. We replace collection.Count with collection. + nodeToBeReplaced1 = actualArgument.Syntax; // collection.Count + replacement1 = expression; // collection + nodeToBeReplaced2 = expectedArgument.Syntax; // 0 + replacement2 = null; + return CountCheckStatus.IsEmpty; + } + else + { + // We have Assert.AreEqual(expectedCount, collection.Count/Length) + // We want Assert.HasCount(expectedCount, collection) + // So, only a single replacement is needed. We replace collection.Count with collection. + nodeToBeReplaced1 = actualArgument.Syntax; // collection.Count + replacement1 = expression; // collection + nodeToBeReplaced2 = null; + replacement2 = null; + return CountCheckStatus.HasCount; + } + } + + nodeToBeReplaced1 = null; + replacement1 = null; + nodeToBeReplaced2 = null; + replacement2 = null; + return CountCheckStatus.Unknown; + } + + private static SyntaxNode? TryGetCollectionExpressionIfBCLCollectionLengthOrCount(IPropertyReferenceOperation propertyReference, INamedTypeSymbol objectTypeSymbol) + => propertyReference.Property.Name is "Count" or "Length" && + propertyReference.Instance?.Type is not null && + (propertyReference.Instance.Type.TypeKind == TypeKind.Array || IsBCLCollectionType(propertyReference.Property.ContainingType, objectTypeSymbol)) + ? propertyReference.Instance.Syntax + : null; + private static bool TryGetFirstArgumentValue(IInvocationOperation operation, [NotNullWhen(true)] out IOperation? argumentValue) => TryGetArgumentValueForParameterOrdinal(operation, 0, out argumentValue); diff --git a/src/Analyzers/MSTest.Analyzers/xlf/Resources.cs.xlf b/src/Analyzers/MSTest.Analyzers/xlf/Resources.cs.xlf index d40df19f9c..e6328c19f1 100644 --- a/src/Analyzers/MSTest.Analyzers/xlf/Resources.cs.xlf +++ b/src/Analyzers/MSTest.Analyzers/xlf/Resources.cs.xlf @@ -132,6 +132,16 @@ Typ deklarující tyto metody by měl také respektovat následující pravidla: Nepoužívejte Assert.AreSame ani Assert.AreNotSame s typy hodnot. + + Replace '{0}' with format parameters with string.Format or string interpolation + Nahraďte {0} parametry formátu pomocí string.Format nebo interpolace řetězce. + + + + Avoid using Assert methods with format parameters + Vyhněte se použití metod Assert s parametry formátu + + Prefer 'Assert.ThrowsExactly' or 'Assert.ThrowsExactlyAsync' over '[ExpectedException]' as it ensures that only the expected call throws the expected exception. The assert APIs also provide more flexibility and allow you to assert extra properties of the exception. Preferujte Assert.ThrowsExactly nebo Assert.ThrowsExactlyAsync před [ExpectedException], protože zajišťuje, že očekávanou výjimku vyvolá pouze očekávané volání. Rozhraní API pro vyhodnocení také poskytují větší flexibilitu a umožňují vyhodnocovat další vlastnosti výjimky. @@ -147,6 +157,16 @@ Typ deklarující tyto metody by měl také respektovat následující pravidla: Vyhněte se [ExpectedException] + + Remove the 'DynamicDataSourceType' argument to use the default auto detect behavior + Pokud chcete použít výchozí chování automatického zjišťování, odeberte argument DynamicDataSourceType. + + + + Avoid passing an explicit 'DynamicDataSourceType' and use the default auto detect behavior + Vyhněte se předávání explicitního typu DynamicDataSourceType a použijte výchozí chování automatického zjišťování. + + Do not assert inside 'async void' methods, local functions, or lambdas. Exceptions that are thrown in this context will be unhandled exceptions. When using VSTest under .NET Framework, they will be silently swallowed. When using Microsoft.Testing.Platform or VSTest under modern .NET, they may crash the process. Nepoužívejte výraz uvnitř metod async void, místních funkcí nebo výrazů lambda. Výjimky vyvolané v tomto kontextu budou neošetřené výjimky. Když používáte VSTest v .NET Framework, budou se bezobslužně používat. Když používáte Microsoft.Testing.Platform nebo VSTest v moderním rozhraní .NET, může dojít k chybovému ukončení procesu. @@ -277,8 +297,13 @@ Typ deklarující tyto metody by měl také respektovat následující pravidla: - DataRow argument type should match method parameter type. Mismatches occur at indices: {0} - Typ argumentu DataRow by měl odpovídat typu parametru metody. V indexech dochází k neshodě: {0} + DataRow argument types do not match method parameter types. {0} + Typy argumentů DataRow neodpovídají typům parametrů metody. {0} + + + + Parameter '{0}' expects type '{1}', but the provided value has type '{2}' + Parametr '{0}' očekává typ '{1}', ale zadaná hodnota má typ '{2}' @@ -402,11 +427,21 @@ Typ deklarující tyto metody by měl také respektovat následující pravidla: Položka [DynamicData] by měla být nastavená jenom u testovací metody. + + '[DynamicData]' member '{0}.{1}' is a field so you should use 'DynamicDataSourceType.AutoDetect' or 'DynamicDataSourceType.Field' (auto detect is the default when not specified explicitly, and is recommended) + Člen [DynamicData] {0}.{1} je pole, měli byste použít DynamicDataSourceType.AutoDetect nebo DynamicDataSourceType.Field (výchozí nastavení je automatické rozpoznávání, pokud není zadané explicitně, a je doporučeno). + + '[DynamicData]' member '{0}.{1}' is a method so you should use 'DynamicDataSourceType.AutoDetect' or 'DynamicDataSourceType.Method' (auto detect is the default when not specified explicitly, and is recommended) '[DynamicData]' člen {0}.{1} je metoda, takže byste měli použít DynamicDataSourceType.AutoDetect nebo DynamicDataSourceType.Method (automatické zjišťování je výchozí, pokud není explicitně zadáno, a doporučuje se) + + '[DynamicData]' member '{0}.{1}' is not a property, method, or field. Only properties, methods, and fields are supported. + Člen [DynamicData] {0}.{1} není vlastnost, metoda ani pole. Podporují se jen vlastnosti, metody a pole. + + '[DynamicData]' member '{0}.{1}' is not a property nor a method. Only properties and methods are supported. '[DynamicData]' člen {0}.{1} není vlastnost ani metoda. Jsou podporovány pouze vlastnosti a metody. @@ -427,6 +462,81 @@ Typ deklarující tyto metody by měl také respektovat následující pravidla: Položka DynamicData by měla být platná. + + When calling async methods that have overloads accepting a CancellationToken parameter, prefer using the overload with TestContext.CancellationToken to enable cooperative cancellation and respect test timeouts. + Při volání asynchronních metod, které mají přetížení akceptující parametr CancellationToken, upřednostňujte použití přetížení s TestContext.CancellationToken, aby se umožnilo kooperativní rušení a dodržování časových limitů testů. + + + + Consider using the overload that accepts a CancellationToken and pass 'TestContext.CancellationToken' + Zvažte použití přetížení, které přijímá CancellationToken a předejte TestContext.CancellationToken. + + + + Flow TestContext.CancellationToken to async operations + Přenesení TestContext.CancellationToken do asynchronních operací + + + + Methods marked with '[GlobalTestInitialize]' or '[GlobalTestCleanup]' should follow the following layout to be valid: +-it can't be declared on a generic class +-it should be 'public' +-it should be 'static' +-it should not be 'async void' +-it should not be a special method (finalizer, operator...). +-it should not be generic +-it should take one parameter of type 'TestContext' +-return type should be 'void', 'Task' or 'ValueTask' + +The type declaring these methods should also respect the following rules: +-The type should be a class +-The class should be 'public' +-The class shouldn't be 'static' +-The class should be marked with '[TestClass]' (or a derived attribute) +-the class should not be generic. + Aby byly metody s označením [GlobalTestInitialize] nebo [GlobalTestCleanup] platné, musí se řídit následujícím rozložením: +– Nesmí být deklarované pro obecnou třídu. +– Musí být „public“. +– Musí být „static“. +– Nesmí být „async void“. +– Nesmí být speciální metodou („finalizer“, „operator“...). +– Nesmí být obecné. +– Musí mít jeden parametr typu TestContext. +– Návratový typ musí být „void“, „Task“ nebo „ValueTask“. + +Typ deklarující tyto metody by měl také respektovat následující pravidla: +– Typ by měl být třída. +– Třída by měla být „public“. +– Třída by neměla být „static“. +– Třída by měla být označena atributem [TestClass] (nebo odvozeným atributem). +– Třída by neměla být obecná. + + + + Global test fixture method '{0}' signature is invalid + Podpis metody globálního testovacího přípravku {0} je neplatný. + + + + GlobalTestInitialize and GlobalTestCleanup methods should have valid layout + Metody GlobalTestInitialize a GlobalTestCleanup by měly mít platné rozložení. + + + + Methods like Contains, StartsWith, and EndsWith return boolean values that indicate whether the condition was met. Ignoring these return values is likely a mistake. + Metody jako Contains, StartsWith a EndsWith vracejí logické hodnoty, které označují, jestli byla podmínka splněna. Ignorování těchto vrácených hodnot je pravděpodobně chyba. + + + + The return value of '{0}' should not be ignored + Návratová hodnota {0} by neměla být ignorována + + + + Do not ignore the return value of string methods + Neignorovat návratovou hodnotu řetězcových metod + + 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“. @@ -437,6 +547,21 @@ Typ deklarující tyto metody by měl také respektovat následující pravidla: Místo trvalého neúspěšného vyhodnocovacího výrazu použijte „Assert.Fail“. + + 'DataTestMethodAttribute' is obsolete and provides no additional functionality over 'TestMethodAttribute'. Use 'TestMethodAttribute' for all test methods, including parameterized tests. + Možnost DataTestMethodAttribute je zastaralá a neposkytuje žádné funkce navíc oproti možnosti TestMethodAttribute. Pro všechny testovací metody, včetně parametrizovaných testů, použijte možnost TestMethodAttribute. + + + + 'DataTestMethod' is obsolete. Use 'TestMethod' instead. + Možnost DataTestMethod je zastaralá. Místo ní použijte možnost TestMethod. + + + + Prefer 'TestMethod' over 'DataTestMethod' + Preferovat TestMethod před DataTestMethod + + Review or remove the assertion as its condition is known to be always true Zkontrolujte nebo odeberte kontrolní výraz, protože jeho podmínka je vždy true. @@ -517,6 +642,16 @@ Typ deklarující tyto metody by měl také respektovat následující pravidla: Veřejné typy by měly být testovací třídy. + + Use 'Assert.{0}' instead of 'StringAssert.{1}' + Použijte Assert.{0}' místo StringAssert.{1}' + + + + Use 'Assert' instead of 'StringAssert' + Použijte Assert místo StringAssert + + Test class '{0}' should be valid Testovací třída {0} by měla být platná. @@ -532,6 +667,21 @@ Typ deklarující tyto metody by měl také respektovat následující pravidla: Signatura {0} metody Test je neplatná. + + TestContext.CancellationToken provides a more direct way to access the cancellation token compared to TestContext.CancellationTokenSource.Token. + TestContext.CancellationToken poskytuje přímější způsob přístupu k tokenu zrušení v porovnání s TestContext.CancellationTokenSource.Token. + + + + Use 'TestContext.CancellationToken' instead of 'TestContext.CancellationTokenSource.Token' + Místo TestContext.CancellationTokenSource.Token použijte TestContext.CancellationToken. + + + + Use TestContext.CancellationToken instead of TestContext.CancellationTokenSource.Token + Místo TestContext.CancellationTokenSource.Token použijte TestContext.CancellationToken. + + Without using 'ClassCleanupBehavior.EndOfClass', the '[ClassCleanup]' will by default be run at the end of the assembly and not at the end of the class. Když nepoužijete ClassCleanupBehavior.EndOfClass, [ClassCleanup] se ve výchozím nastavení spustí na konci sestavení, nikoli na konci třídy. @@ -720,12 +870,12 @@ Typ deklarující tyto metody by měl také respektovat následující pravidla: - Type contaning '[TestMethod]' should be marked with '[TestClass]', otherwise the test method will be silently ignored. - Typ obsahující [TestMethod] by měl mít označení [TestClass], jinak bude testovací metoda bez jakéhokoli upozornění ignorována. + Type containing '[TestMethod]' should be marked with '[TestClass]', otherwise the test method will be silently ignored. + Typ obsahující [TestMethod] by měl mít označení [TestClass], jinak se bude testovací metoda bez upozornění ignorovat. - Class '{0}' contains test methods and should be marked with '[TestClass]' + Type '{0}' contains test methods and should be marked with '[TestClass]' Třída {0} obsahuje testovací metody a měla by mít označení [TestClass]. @@ -744,6 +894,11 @@ Typ deklarující tyto metody by měl také respektovat následující pravidla: Asynchronní testovací metody nevyžadují příponu Async. + + TestContext parameter is required by MSTest for AssemblyInitialize and ClassInitialize methods + Parametr TestContext vyžaduje MSTest pro metody AssemblyInitialize a ClassInitialize. + + [{0}] can only be set on methods marked with [TestMethod] [{0}] lze nastavit pouze u metod označených pomocí metody [TestMethod]. @@ -764,6 +919,21 @@ Typ deklarující tyto metody by měl také respektovat následující pravidla: Použít ConditionBaseAttribute u testovacích tříd + + Using '[Timeout]' without explicitly setting 'CooperativeCancellation = true' is discouraged. In a future version, cooperative cancellation will become the default behavior. Set 'CooperativeCancellation = true' to opt into the recommended behavior and avoid breaking changes. + Použití „[Timeout]“ bez explicitního nastavení „CooperativeCancellation = true“ se nedoporučuje. V budoucí verzi se kooperativní zrušení stane výchozím chováním. Nastavte „CooperativeCancellation = true“, abyste zapnuli doporučené chování a vyhnuli se změnám, které by mohly způsobit problémy. + + + + Use 'CooperativeCancellation = true' with '[Timeout]' to enable cooperative cancellation behavior + Použijte „CooperativeCancellation = true“ s „[Timeout]“, abyste povolili chování kooperativního zrušení + + + + Use 'CooperativeCancellation = true' with '[Timeout]' + Použijte „CooperativeCancellation = true“ s „[Timeout]“ + + '[DeploymentItem]' can be specified only on test class or test method [DeploymentItem] se dá zadat jenom pro testovací třídu nebo testovací metodu. @@ -819,6 +989,36 @@ Typ deklarující tyto metody by měl také respektovat následující pravidla: Použití atributu opakování u testovací metody + + TestContext property cannot be accessed in this context + Vlastnost TestContext není v tomto kontextu přístupná + + + + TestContext property '{0}' cannot be accessed in '{1}' method + Vlastnost TestContext {0} není přístupná v metodě {1} + + + + Some TestContext properties are only available during test execution and cannot be accessed in assembly initialize, class initialize, class cleanup, or assembly cleanup methods. + Některé vlastnosti TestContext jsou k dispozici pouze během provádění testu a nelze k nim přistupovat v metodách inicializace sestavení, inicializace třídy, vyčištění třídy nebo vyčištění sestavení. + + + + Assert.Throws methods should contain only a single statement or expression. Multiple statements can be misleading - if the first statement throws, subsequent statements are never executed; if it doesn't throw, it should be moved outside the Assert.Throws. + Metody Assert.Throws by měly obsahovat pouze jeden příkaz nebo výraz. Více příkazů může být zavádějící – pokud se vyvolá první příkaz, následující příkazy se nikdy neprovedou. Pokud se nevyvolá, měl by se přesunout mimo Assert.Throws. + + + + Assert.Throws should contain only a single statement/expression + Parametr Assert.Throws by měl obsahovat jenom jeden příkaz nebo výraz + + + + Assert.Throws should contain only a single statement/expression + Parametr Assert.Throws by měl obsahovat jenom jeden příkaz nebo výraz + + \ No newline at end of file diff --git a/src/Analyzers/MSTest.Analyzers/xlf/Resources.de.xlf b/src/Analyzers/MSTest.Analyzers/xlf/Resources.de.xlf index ed0272a850..ec569abfd5 100644 --- a/src/Analyzers/MSTest.Analyzers/xlf/Resources.de.xlf +++ b/src/Analyzers/MSTest.Analyzers/xlf/Resources.de.xlf @@ -132,6 +132,16 @@ Der Typ, der diese Methoden deklariert, sollte auch die folgenden Regeln beachte Verwenden Sie "Assert.AreSame" oder "Assert.AreNotSame" nicht mit Werttypen. + + Replace '{0}' with format parameters with string.Format or string interpolation + „{0}“ durch Formatparameter mit string.Format oder Zeichenfolgeninterpolation ersetzen + + + + Avoid using Assert methods with format parameters + Verwendung von Assert-Methoden mit Formatparametern vermeiden + + Prefer 'Assert.ThrowsExactly' or 'Assert.ThrowsExactlyAsync' over '[ExpectedException]' as it ensures that only the expected call throws the expected exception. The assert APIs also provide more flexibility and allow you to assert extra properties of the exception. „Assert.ThrowsExactly“ oder „Assert.ThrowsExactlyAsync“ gegenüber „[ExpectedException]“ bevorzugen, da dadurch sichergestellt wird, dass nur der erwartete Aufruf die erwartete Ausnahme auslöst. Die Assert-APIs bieten außerdem mehr Flexibilität und ermöglichen es Ihnen, zusätzliche Eigenschaften der Ausführung zu bestätigen. @@ -147,6 +157,16 @@ Der Typ, der diese Methoden deklariert, sollte auch die folgenden Regeln beachte „[ExpectedException]“ vermeiden + + Remove the 'DynamicDataSourceType' argument to use the default auto detect behavior + Entfernen Sie das Argument „DynamicDataSourceType“, um das standardmäßige automatische Erkennungsverhalten zu verwenden. + + + + Avoid passing an explicit 'DynamicDataSourceType' and use the default auto detect behavior + Vermeiden Sie die explizite Übergabe von „DynamicDataSourceType” und verwenden Sie das standardmäßige automatische Erkennungsverhalten. + + Do not assert inside 'async void' methods, local functions, or lambdas. Exceptions that are thrown in this context will be unhandled exceptions. When using VSTest under .NET Framework, they will be silently swallowed. When using Microsoft.Testing.Platform or VSTest under modern .NET, they may crash the process. Nicht innerhalb von "async void"-Methoden, lokalen Funktionen oder Lambdafunktionen bestätigen. Ausnahmen, die in diesem Kontext ausgelöst werden, werden nicht behandelt. Bei Verwendung von VSTest unter .NET Framework werden diese im Hintergrund verschlungen. Wenn Sie Microsoft.Testing.Platform oder VSTest unter modernem .NET verwenden, kann der Prozess abgestürzt werden. @@ -278,8 +298,13 @@ Der Typ, der diese Methoden deklariert, sollte auch die folgenden Regeln beachte - DataRow argument type should match method parameter type. Mismatches occur at indices: {0} - Der DataRow-Argumenttyp muss mit dem Methodenparametertyp übereinstimmen. Nichtübereinstimmungen treten bei Indizes auf: {0} + DataRow argument types do not match method parameter types. {0} + DataRow-Argumenttypen stimmen nicht mit Methodenparametertypen überein. {0} + + + + Parameter '{0}' expects type '{1}', but the provided value has type '{2}' + Der Parameter „{0}“ erwartet den Typ „{1}“, aber der übergebene Wert hat den Typ „{2}“. @@ -403,11 +428,21 @@ Der Typ, der diese Methoden deklariert, sollte auch die folgenden Regeln beachte "[DynamicData]" sollte nur für eine Testmethode festgelegt werden. + + '[DynamicData]' member '{0}.{1}' is a field so you should use 'DynamicDataSourceType.AutoDetect' or 'DynamicDataSourceType.Field' (auto detect is the default when not specified explicitly, and is recommended) + '[DynamicData]' Mitglied '{0}.{1}' ist ein Feld, daher sollten Sie 'DynamicDataSourceType.AutoDetect' oder 'DynamicDataSourceType.Field' verwenden (die automatische Erkennung ist der Standardwert, wenn sie nicht explizit angegeben wird und wird empfohlen). + + '[DynamicData]' member '{0}.{1}' is a method so you should use 'DynamicDataSourceType.AutoDetect' or 'DynamicDataSourceType.Method' (auto detect is the default when not specified explicitly, and is recommended) '[DynamicData]' Member "{0}.{1}" ist eine Methode, daher sollten Sie "DynamicDataSourceType.AutoDetect" oder "DynamicDataSourceType.Method" verwenden (die automatische Erkennung ist der Standardwert, wenn sie nicht explizit angegeben wird und empfohlen wird). + + '[DynamicData]' member '{0}.{1}' is not a property, method, or field. Only properties, methods, and fields are supported. + '[DynamicData]' Mitglied '{0}.{1}' ist keine Eigenschaft, Methode oder kein Feld. Nur Eigenschaften, Methoden und Felder werden unterstützt. + + '[DynamicData]' member '{0}.{1}' is not a property nor a method. Only properties and methods are supported. '[DynamicData]' Member "{0}.{1}" ist weder eine Eigenschaft noch eine Methode. Nur Eigenschaften und Methoden werden unterstützt. @@ -428,6 +463,81 @@ Der Typ, der diese Methoden deklariert, sollte auch die folgenden Regeln beachte DynamicData muss gültig sein. + + When calling async methods that have overloads accepting a CancellationToken parameter, prefer using the overload with TestContext.CancellationToken to enable cooperative cancellation and respect test timeouts. + Wenn Sie asynchrone Methoden aufrufen, die über Überladungen verfügen, die einen CancellationToken-Parameter akzeptieren, bevorzugen Sie die Verwendung der Überladung mit TestContext.CancellationToken, um einen kooperativen Abbruch zu ermöglichen und Testtimeouts zu berücksichtigen. + + + + Consider using the overload that accepts a CancellationToken and pass 'TestContext.CancellationToken' + Verwendung der Überladung erwägen, die ein CancellationToken akzeptiert, und „TestContext.CancellationToken“ übergeben + + + + Flow TestContext.CancellationToken to async operations + Fluss von TestContext.CancellationToken zu asynchronen Vorgängen + + + + Methods marked with '[GlobalTestInitialize]' or '[GlobalTestCleanup]' should follow the following layout to be valid: +-it can't be declared on a generic class +-it should be 'public' +-it should be 'static' +-it should not be 'async void' +-it should not be a special method (finalizer, operator...). +-it should not be generic +-it should take one parameter of type 'TestContext' +-return type should be 'void', 'Task' or 'ValueTask' + +The type declaring these methods should also respect the following rules: +-The type should be a class +-The class should be 'public' +-The class shouldn't be 'static' +-The class should be marked with '[TestClass]' (or a derived attribute) +-the class should not be generic. + Methoden, die mit „[GlobalTestInitialize]“ oder „[GlobalTestCleanup]“ gekennzeichnet sind, sollten dem folgenden Layout folgen, um gültig zu sein: +– kann nicht für eine generische Klasse deklariert werden +– muss „public“ sein +– muss „static“ sein +– darf nicht „async void“ sein +– darf keine spezielle Methode sein (Finalizer, Operator...). +– darf nicht „generic“ sein +– muss einen Parameter vom Typ „TestContext“ annehmen +– der Rückgabetyp muss „void“, „Task“ oder „ValueTask“ sein + +Der Typ, der diese Methoden deklariert, sollte auch die folgenden Regeln beachten: +– Der Typ sollte eine Klasse sein +– Die Klasse sollte „public“ sein +– Die Klasse darf nicht „static“ sein +– Die Klasse muss mit „[TestClass]“ (oder einem abgeleiteten Attribut) markiert werden +– die Klasse darf nicht generisch sein. + + + + Global test fixture method '{0}' signature is invalid + Die Signatur der globalen Testfixierungsmethode „{0}“ ist ungültig + + + + GlobalTestInitialize and GlobalTestCleanup methods should have valid layout + Die Methoden „GlobalTestInitialize“ und „GlobalTestCleanup“ müssen ein gültiges Layout aufweisen + + + + Methods like Contains, StartsWith, and EndsWith return boolean values that indicate whether the condition was met. Ignoring these return values is likely a mistake. + Methoden wie Contains, StartsWith und EndsWith geben boolesche Werte zurück, die angeben, ob die Bedingung erfüllt wurde. Das Ignorieren dieser Rückgabewerte ist wahrscheinlich ein Fehler. + + + + The return value of '{0}' should not be ignored + Der Rückgabewert von „{0}“ darf nicht ignoriert werden + + + + Do not ignore the return value of string methods + Rückgabewert von Zeichenfolgenmethoden nicht ignorieren + + 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. @@ -438,6 +548,21 @@ Der Typ, der diese Methoden deklariert, sollte auch die folgenden Regeln beachte Verwenden Sie „Assert.Fail“ anstelle einer Assert-Anweisung, bei der immer ein Fehler auftritt. + + 'DataTestMethodAttribute' is obsolete and provides no additional functionality over 'TestMethodAttribute'. Use 'TestMethodAttribute' for all test methods, including parameterized tests. + „DataTestMethodAttribute“ ist veraltet und bietet keine zusätzliche Funktionalität im Vergleich zu „TestMethodAttribute“. Verwenden Sie „TestMethodAttribute“ für alle Testmethoden, einschließlich parametrisierter Tests. + + + + 'DataTestMethod' is obsolete. Use 'TestMethod' instead. + „DataTestMethod“ ist veraltet. Verwenden Sie stattdessen „TestMethod“. + + + + Prefer 'TestMethod' over 'DataTestMethod' + „TestMethod“ gegenüber „DataTestMethod“ bevorzugen + + Review or remove the assertion as its condition is known to be always true Überprüfen oder entfernen Sie die Assertion, weil ihre Bedingung bekanntermaßen immer TRUE ist. @@ -518,6 +643,16 @@ Der Typ, der diese Methoden deklariert, sollte auch die folgenden Regeln beachte Öffentliche Typen sollten Testklassen sein. + + Use 'Assert.{0}' instead of 'StringAssert.{1}' + Verwenden Sie „Assert.{0}“ anstelle von „StringAssert.{1}“. + + + + Use 'Assert' instead of 'StringAssert' + Verwenden Sie „Assert“ anstelle von „StringAssert“. + + Test class '{0}' should be valid Die Testklasse „{0}“ muss gültig sein @@ -533,6 +668,21 @@ Der Typ, der diese Methoden deklariert, sollte auch die folgenden Regeln beachte Signatur der Testmethode "{0}" ist ungültig + + TestContext.CancellationToken provides a more direct way to access the cancellation token compared to TestContext.CancellationTokenSource.Token. + TestContext.CancellationToken bietet im Vergleich mit TestContext.CancellationTokenSource.Token eine direktere Möglichkeit, auf das Abbruchtoken zuzugreifen. + + + + Use 'TestContext.CancellationToken' instead of 'TestContext.CancellationTokenSource.Token' + „TestContext.CancellationToken“ anstelle von „TestContext.CancellationTokenSource.Token“ verwenden + + + + Use TestContext.CancellationToken instead of TestContext.CancellationTokenSource.Token + TestContext.CancellationToken anstelle von TestContext.CancellationTokenSource.Token verwenden + + Without using 'ClassCleanupBehavior.EndOfClass', the '[ClassCleanup]' will by default be run at the end of the assembly and not at the end of the class. Ohne Verwendung von „ClassCleanupBehavior.EndOfClass“ wird „[ClassCleanup]“ standardmäßig am Ende der Assembly und nicht am Ende der Klasse ausgeführt. @@ -721,13 +871,13 @@ Der Typ, der diese Methoden deklariert, sollte auch die folgenden Regeln beachte - Type contaning '[TestMethod]' should be marked with '[TestClass]', otherwise the test method will be silently ignored. - Der Typ, der "[TestMethod]" enthält, sollte mit "[TestClass]" gekennzeichnet werden, andernfalls wird die Testmethode im Hintergrund ignoriert. + Type containing '[TestMethod]' should be marked with '[TestClass]', otherwise the test method will be silently ignored. + Der Typ, der „[Testmethode]“ enthält, sollte mit „[Testklasse]“ gekennzeichnet werden, andernfalls wird die Testmethode still im Hintergrund ignoriert. - Class '{0}' contains test methods and should be marked with '[TestClass]' - Die Klasse "{0}" enthält Testmethoden und muss mit "[TestClass]" gekennzeichnet werden. + Type '{0}' contains test methods and should be marked with '[TestClass]' + Der Typ „{0}“ enthält Testmethoden und sollte mit „[TestClass]“ gekennzeichnet werden @@ -745,6 +895,11 @@ Der Typ, der diese Methoden deklariert, sollte auch die folgenden Regeln beachte Für asynchrone Testmethoden ist das Suffix "Async" nicht erforderlich. + + TestContext parameter is required by MSTest for AssemblyInitialize and ClassInitialize methods + Der TestContext-Parameter ist für MSTest für AssemblyInitialize- und ClassInitialize-Methoden erforderlich + + [{0}] can only be set on methods marked with [TestMethod] [{0}] kann nur für Methoden festgelegt werden, die mit [TestMethod] markiert sind. @@ -765,6 +920,21 @@ Der Typ, der diese Methoden deklariert, sollte auch die folgenden Regeln beachte "ConditionBaseAttribute" für Testklassen verwenden + + Using '[Timeout]' without explicitly setting 'CooperativeCancellation = true' is discouraged. In a future version, cooperative cancellation will become the default behavior. Set 'CooperativeCancellation = true' to opt into the recommended behavior and avoid breaking changes. + Es wird davon abgeraten, „[Timeout]“ ohne die explizite Festlegung von „CooperativeCancellation = true“ zu verwenden. In einer zukünftigen Version wird der kooperative Abbruch das Standardverhalten sein. Legen Sie „CooperativeCancellation = true“ fest, um das empfohlene Verhalten zu aktivieren und Breaking Changes zu vermeiden. + + + + Use 'CooperativeCancellation = true' with '[Timeout]' to enable cooperative cancellation behavior + Verwenden Sie „CooperativeCancellation = true“ mit „[Timeout]“, um das kooperative Abbruchverhalten zu aktivieren. + + + + Use 'CooperativeCancellation = true' with '[Timeout]' + Verwenden Sie „CooperativeCancellation = true“ mit „[Timeout]“. + + '[DeploymentItem]' can be specified only on test class or test method „[DeploymentItem]“ kann nur für die Testklasse oder Testmethode angegeben werden. @@ -820,6 +990,36 @@ Der Typ, der diese Methoden deklariert, sollte auch die folgenden Regeln beachte Verwenden des Wiederholungsattributs für die Testmethode + + TestContext property cannot be accessed in this context + Auf die TestContext-Eigenschaft kann in diesem Kontext nicht zugegriffen werden + + + + TestContext property '{0}' cannot be accessed in '{1}' method + Auf die TestContext-Eigenschaft „{0}“ kann in der Methode „{1}“ nicht zugegriffen werden + + + + Some TestContext properties are only available during test execution and cannot be accessed in assembly initialize, class initialize, class cleanup, or assembly cleanup methods. + Einige TestContext-Eigenschaften sind nur während der Testausführung verfügbar und können nicht in Assemblyinitialisierungs-, Klasseninitialisierungs-, Klassenbereinigungs- oder Assemblybereinigungsmethoden aufgerufen werden. + + + + Assert.Throws methods should contain only a single statement or expression. Multiple statements can be misleading - if the first statement throws, subsequent statements are never executed; if it doesn't throw, it should be moved outside the Assert.Throws. + Assert.Throws-Methoden dürfen nur eine einzelne Anweisung oder einen einzelnen Ausdruck enthalten. Mehrere Anweisungen können irreführend sein: Wenn die erste Anweisung eine Ausnahme auslöst, werden nachfolgende Anweisungen nie ausgeführt; wenn sie keine Ausnahme auslöst, sollte sie nach außerhalb von Assert.Throws verschoben werden. + + + + Assert.Throws should contain only a single statement/expression + Assert.Throws darf nur eine einzelne Anweisung/einen einzelnen Ausdruck enthalten + + + + Assert.Throws should contain only a single statement/expression + Assert.Throws darf nur eine einzelne Anweisung/einen einzelnen Ausdruck enthalten + + \ No newline at end of file diff --git a/src/Analyzers/MSTest.Analyzers/xlf/Resources.es.xlf b/src/Analyzers/MSTest.Analyzers/xlf/Resources.es.xlf index 75d54fde75..8509932845 100644 --- a/src/Analyzers/MSTest.Analyzers/xlf/Resources.es.xlf +++ b/src/Analyzers/MSTest.Analyzers/xlf/Resources.es.xlf @@ -132,6 +132,16 @@ El tipo que declara estos métodos también debe respetar las reglas siguientes: No use "Assert.AreSame" o "Assert.AreNotSame" con tipos de valor + + Replace '{0}' with format parameters with string.Format or string interpolation + Reemplace '{0}' con parámetros de formato utilizando string.Format o interpolación de cadenas + + + + Avoid using Assert methods with format parameters + Evite el uso de métodos Assert con parámetros de formato + + Prefer 'Assert.ThrowsExactly' or 'Assert.ThrowsExactlyAsync' over '[ExpectedException]' as it ensures that only the expected call throws the expected exception. The assert APIs also provide more flexibility and allow you to assert extra properties of the exception. Preferir "Assert.ThrowsExactly" o "Assert.ThrowsExactlyAsync" en lugar de "[ExpectedException]", ya que garantiza que solo la llamada esperada inicie la excepción esperada. Las API de aserción también proporcionan más flexibilidad y permiten declarar propiedades adicionales de la ejecución. @@ -147,6 +157,16 @@ El tipo que declara estos métodos también debe respetar las reglas siguientes: Evitar '[ExpectedException]' + + Remove the 'DynamicDataSourceType' argument to use the default auto detect behavior + Elimine el argumento 'DynamicDataSourceType' para utilizar el comportamiento de detección automática predeterminado + + + + Avoid passing an explicit 'DynamicDataSourceType' and use the default auto detect behavior + Evite pasar un 'DynamicDataSourceType' explícito y utilice el comportamiento de detección automática predeterminado + + Do not assert inside 'async void' methods, local functions, or lambdas. Exceptions that are thrown in this context will be unhandled exceptions. When using VSTest under .NET Framework, they will be silently swallowed. When using Microsoft.Testing.Platform or VSTest under modern .NET, they may crash the process. No realice ninguna aserción dentro de métodos 'async void', funciones locales o expresiones lambda. Las excepciones que se inicien en este contexto serán excepciones no controladas. Al usar VSTest en .NET Framework, se ingerirán silenciosamente. Cuando se usa Microsoft.Testing.Platform o VSTest en .NET moderno, es posible que se bloquee el proceso. @@ -277,8 +297,13 @@ El tipo que declara estos métodos también debe respetar las reglas siguientes: - DataRow argument type should match method parameter type. Mismatches occur at indices: {0} - El tipo de argumento DataRow debe coincidir con el tipo de parámetro de método. Se producen errores de coincidencia en los índices: {0} + DataRow argument types do not match method parameter types. {0} + Los tipos de argumentos DataRow no coinciden con los tipos de parámetro de método. {0} + + + + Parameter '{0}' expects type '{1}', but the provided value has type '{2}' + El parámetro '{0}' espera el tipo '{1}', pero el valor proporcionado tiene el tipo '{2}' @@ -402,11 +427,21 @@ El tipo que declara estos métodos también debe respetar las reglas siguientes: '[DynamicData]' solo debe establecerse en un método de prueba + + '[DynamicData]' member '{0}.{1}' is a field so you should use 'DynamicDataSourceType.AutoDetect' or 'DynamicDataSourceType.Field' (auto detect is the default when not specified explicitly, and is recommended) + El miembro '{0}.{1}' de '[DynamicData]' es un campo, por lo que debe usar 'DynamicDataSourceType.AutoDetect' o 'DynamicDataSourceType.Field' (la detección automática es el valor predeterminado cuando no se especifica explícitamente y se recomienda) + + '[DynamicData]' member '{0}.{1}' is a method so you should use 'DynamicDataSourceType.AutoDetect' or 'DynamicDataSourceType.Method' (auto detect is the default when not specified explicitly, and is recommended) '[DynamicData]' miembro '{0}.{1}' es un método, por lo que debe usar 'DynamicDataSourceType.AutoDetect' o 'DynamicDataSourceType.Method' (la detección automática es el valor predeterminado cuando no se especifica explícitamente y se recomienda) + + '[DynamicData]' member '{0}.{1}' is not a property, method, or field. Only properties, methods, and fields are supported. + El miembro '{0}.{1}' de '[DynamicData]' no es una propiedad, un método o un campo. Solo se admiten propiedades, métodos y campos. + + '[DynamicData]' member '{0}.{1}' is not a property nor a method. Only properties and methods are supported. '[DynamicData]' miembro '{0}.{1}' no es una propiedad ni un método. Solo se admiten propiedades y métodos. @@ -427,6 +462,81 @@ El tipo que declara estos métodos también debe respetar las reglas siguientes: DynamicData debe ser válido + + When calling async methods that have overloads accepting a CancellationToken parameter, prefer using the overload with TestContext.CancellationToken to enable cooperative cancellation and respect test timeouts. + Al llamar a métodos asincrónicos que tienen sobrecargas que aceptan un parámetro CancellationToken, es preferible utilizar la sobrecarga con TestContext.CancellationToken para habilitar la cancelación cooperativa y respetar los tiempos de espera de las pruebas. + + + + Consider using the overload that accepts a CancellationToken and pass 'TestContext.CancellationToken' + Considere utilizar la sobrecarga que acepta un CancellationToken y pase 'TestContext.CancellationToken' + + + + Flow TestContext.CancellationToken to async operations + Flujo de TestContext.CancellationToken a operaciones asincrónicas + + + + Methods marked with '[GlobalTestInitialize]' or '[GlobalTestCleanup]' should follow the following layout to be valid: +-it can't be declared on a generic class +-it should be 'public' +-it should be 'static' +-it should not be 'async void' +-it should not be a special method (finalizer, operator...). +-it should not be generic +-it should take one parameter of type 'TestContext' +-return type should be 'void', 'Task' or 'ValueTask' + +The type declaring these methods should also respect the following rules: +-The type should be a class +-The class should be 'public' +-The class shouldn't be 'static' +-The class should be marked with '[TestClass]' (or a derived attribute) +-the class should not be generic. + Los métodos marcados con '[GlobalTestInitialize]' o '[GlobalTestCleanup]' deben seguir el siguiente diseño para ser válidos: +-no se puede declarar en una clase genérica +- debería ser 'público' +- debería estar 'estático' +-no debe ser 'async void' +-no debe ser un método especial (finalizador, operador...). +-no debe ser genérico +-debe tomar un parámetro de tipo 'TestContext' +- El tipo de valor devuelto debe ser 'void', 'Task' o 'ValueTask' + +El tipo que declara estos métodos también debe respetar las reglas siguientes: +-El tipo debe ser una clase +-La clase debe ser 'public' +-La clase no debe ser 'static' +-La clase debe marcarse con '[TestClass]' (o un atributo derivado) +-la clase no debe ser genérica. + + + + Global test fixture method '{0}' signature is invalid + La firma del método del accesorio de pruebas global '{0}' no es válida + + + + GlobalTestInitialize and GlobalTestCleanup methods should have valid layout + Los métodos GlobalTestInitialize y GlobalTestCleanup deben tener un diseño válido + + + + Methods like Contains, StartsWith, and EndsWith return boolean values that indicate whether the condition was met. Ignoring these return values is likely a mistake. + Los métodos como Contains, StartsWith y EndsWith devuelven valores booleanos que indican si se cumplió la condición. Ignorar estos valores devueltos probablemente sea un error. + + + + The return value of '{0}' should not be ignored + No se debe ignorar el valor devuelto de '{0}' + + + + Do not ignore the return value of string methods + No ignore el valor devuelto de los métodos de cadena + + 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 @@ -437,6 +547,21 @@ El tipo que declara estos métodos también debe respetar las reglas siguientes: Usar "Assert.Fail" en lugar de una aserción que siempre tiene errores + + 'DataTestMethodAttribute' is obsolete and provides no additional functionality over 'TestMethodAttribute'. Use 'TestMethodAttribute' for all test methods, including parameterized tests. + "DataTestMethodAttribute" está obsoleto y no proporciona ninguna funcionalidad adicional sobre "TestMethodAttribute". Use "TestMethodAttribute" para todos los métodos de prueba, incluidas las pruebas parametrizadas. + + + + 'DataTestMethod' is obsolete. Use 'TestMethod' instead. + "DataTestMethod" está obsoleto. Use "TestMethod" en su lugar. + + + + Prefer 'TestMethod' over 'DataTestMethod' + Preferir "TestMethod" a "DataTestMethod" + + Review or remove the assertion as its condition is known to be always true Revise o quite la aserción porque se sabe que su condición siempre es true @@ -517,6 +642,16 @@ El tipo que declara estos métodos también debe respetar las reglas siguientes: Los tipos públicos deben ser clases de prueba + + Use 'Assert.{0}' instead of 'StringAssert.{1}' + Use 'Assert'{0}. en lugar de 'StringAssert.'{1} + + + + Use 'Assert' instead of 'StringAssert' + Usar 'Assert' en lugar de 'StringAssert' + + Test class '{0}' should be valid La clase de prueba '{0}' debe ser válida @@ -532,6 +667,21 @@ El tipo que declara estos métodos también debe respetar las reglas siguientes: La firma del método de prueba '{0}' no es válida + + TestContext.CancellationToken provides a more direct way to access the cancellation token compared to TestContext.CancellationTokenSource.Token. + TestContext.CancellationToken proporciona una manera más directa de acceder al token de cancelación en comparación con TestContext.CancellationTokenSource.Token. + + + + Use 'TestContext.CancellationToken' instead of 'TestContext.CancellationTokenSource.Token' + Use 'TestContext.CancellationToken' en lugar de 'TestContext.CancellationTokenSource.Token' + + + + Use TestContext.CancellationToken instead of TestContext.CancellationTokenSource.Token + Use TestContext.CancellationToken en lugar de TestContext.CancellationTokenSource.Token + + Without using 'ClassCleanupBehavior.EndOfClass', the '[ClassCleanup]' will by default be run at the end of the assembly and not at the end of the class. Sin usar 'ClassCleanupBehavior.EndOfClass', el '[ClassCleanup]' se ejecutará de forma predeterminada al final del ensamblado y no al final de la clase. @@ -720,13 +870,13 @@ El tipo que declara estos métodos también debe respetar las reglas siguientes: - Type contaning '[TestMethod]' should be marked with '[TestClass]', otherwise the test method will be silently ignored. + Type containing '[TestMethod]' should be marked with '[TestClass]', otherwise the test method will be silently ignored. El tipo que contiene '[TestMethod]' debe marcarse con '[TestClass]'; de lo contrario, el método de prueba se omitirá de forma silenciosa. - Class '{0}' contains test methods and should be marked with '[TestClass]' - La clase '{0}' contiene métodos de prueba y debe marcarse con '[TestClass]' + Type '{0}' contains test methods and should be marked with '[TestClass]' + El tipo '{0}' contiene métodos de prueba y debe marcarse con '[TestClass]' @@ -744,6 +894,11 @@ El tipo que declara estos métodos también debe respetar las reglas siguientes: Los métodos de prueba asincrónicos no requieren el sufijo "Async". + + TestContext parameter is required by MSTest for AssemblyInitialize and ClassInitialize methods + MSTest requiere el parámetro TestContext para los métodos AssemblyInitialize y ClassInitialize + + [{0}] can only be set on methods marked with [TestMethod] [{0}] solo se puede establecer en métodos marcados con [TestMethod] @@ -764,6 +919,21 @@ El tipo que declara estos métodos también debe respetar las reglas siguientes: Usar 'ConditionBaseAttribute' en clases de prueba + + Using '[Timeout]' without explicitly setting 'CooperativeCancellation = true' is discouraged. In a future version, cooperative cancellation will become the default behavior. Set 'CooperativeCancellation = true' to opt into the recommended behavior and avoid breaking changes. + No se recomienda usar "[Timeout]" sin establecer explícitamente "CooperativeCancellation = true". En una versión futura, la cancelación cooperativa se convertirá en el comportamiento predeterminado. Establece "CooperativeCancellation = true" para participar en el comportamiento recomendado y evitar cambios importantes. + + + + Use 'CooperativeCancellation = true' with '[Timeout]' to enable cooperative cancellation behavior + Usa "CooperativeCancellation = true" con "[Timeout]" para habilitar el comportamiento de cancelación cooperativa. + + + + Use 'CooperativeCancellation = true' with '[Timeout]' + Usa "CooperativeCancellation = true" con "[Timeout]" + + '[DeploymentItem]' can be specified only on test class or test method '[DeploymentItem]' solo se puede especificar en la clase de prueba o el método de prueba @@ -819,6 +989,36 @@ El tipo que declara estos métodos también debe respetar las reglas siguientes: Usar el atributo de reintento en el método de prueba + + TestContext property cannot be accessed in this context + No se puede tener acceso a la propiedad TestContext en este contexto + + + + TestContext property '{0}' cannot be accessed in '{1}' method + No se puede tener acceso a la propiedad TestContext "{0}" en el método "{1}" + + + + Some TestContext properties are only available during test execution and cannot be accessed in assembly initialize, class initialize, class cleanup, or assembly cleanup methods. + Algunas propiedades de TestContext solo están disponibles durante la ejecución de la prueba y no se puede acceder a ellas en los métodos assembly initialize, class initialize, class cleanup o assembly cleanup. + + + + Assert.Throws methods should contain only a single statement or expression. Multiple statements can be misleading - if the first statement throws, subsequent statements are never executed; if it doesn't throw, it should be moved outside the Assert.Throws. + Los métodos Assert.Throws solo deben contener una sola instrucción o expresión. Varias instrucciones pueden ser engañosas: si se lanza la primera instrucción, las instrucciones posteriores nunca se ejecutan; si no se lanza, se debe mover fuera de Assert.Throws. + + + + Assert.Throws should contain only a single statement/expression + Assert.Throws debe contener solo una única instrucción o expresión + + + + Assert.Throws should contain only a single statement/expression + Assert.Throws debe contener solo una única instrucción o expresión + + \ No newline at end of file diff --git a/src/Analyzers/MSTest.Analyzers/xlf/Resources.fr.xlf b/src/Analyzers/MSTest.Analyzers/xlf/Resources.fr.xlf index 8bdc50155d..8a75b123d9 100644 --- a/src/Analyzers/MSTest.Analyzers/xlf/Resources.fr.xlf +++ b/src/Analyzers/MSTest.Analyzers/xlf/Resources.fr.xlf @@ -132,6 +132,16 @@ Le type doit être une classe N’utilisez pas 'Assert.AreSame' ou 'Assert.AreNotSame' avec des types valeur + + Replace '{0}' with format parameters with string.Format or string interpolation + Remplacez « {0} » par des paramètres de format par une chaîne. Mise en forme ou interpolation de chaîne + + + + Avoid using Assert methods with format parameters + Évitez d’utiliser des méthodes Assert avec des paramètres de format + + Prefer 'Assert.ThrowsExactly' or 'Assert.ThrowsExactlyAsync' over '[ExpectedException]' as it ensures that only the expected call throws the expected exception. The assert APIs also provide more flexibility and allow you to assert extra properties of the exception. Préférez « Assert.ThrowsExactly » ou « Assert.ThrowsExactlyAsync » à « [ExpectedException] », car il garantit que seul l’appel attendu lève l’exception attendue. Les API d’assertion offrent également plus de flexibilité et vous permettent de déclarer des propriétés supplémentaires de l’exception. @@ -147,6 +157,16 @@ Le type doit être une classe Éviter « [ExpectedException] » + + Remove the 'DynamicDataSourceType' argument to use the default auto detect behavior + Supprimez l’argument « DynamicDataSourceType » pour utiliser le comportement de détection automatique par défaut + + + + Avoid passing an explicit 'DynamicDataSourceType' and use the default auto detect behavior + Évitez de passer un type « DynamicDataSourceType » explicite et utilisez le comportement de détection automatique par défaut + + Do not assert inside 'async void' methods, local functions, or lambdas. Exceptions that are thrown in this context will be unhandled exceptions. When using VSTest under .NET Framework, they will be silently swallowed. When using Microsoft.Testing.Platform or VSTest under modern .NET, they may crash the process. N’effectuez pas d’assertion dans les méthodes 'async void', les fonctions locales ou les expressions lambda. Les exceptions levées dans ce contexte seront des exceptions non gérées. Lors de l’utilisation de VSTest sous .NET Framework, ils sont silencieusement coupés. Quand vous utilisez Microsoft.Testing.Platform ou VSTest sous .NET moderne, ils peuvent bloquer le processus. @@ -277,8 +297,13 @@ Le type doit être une classe - DataRow argument type should match method parameter type. Mismatches occur at indices: {0} - Le type d’argument DataRow doit correspondre au type de paramètre de la méthode. Des incompatibilités se produisent aux index :{0} + DataRow argument types do not match method parameter types. {0} + Les types d’argument DataRow ne correspondent pas aux types de paramètre de la méthode. {0} + + + + Parameter '{0}' expects type '{1}', but the provided value has type '{2}' + Le paramètre « {0} » attend un type « {1} », mais la valeur fournie a un type « {2} » @@ -402,11 +427,21 @@ Le type doit être une classe '[DynamicData]' ne doit être défini que sur une méthode de test + + '[DynamicData]' member '{0}.{1}' is a field so you should use 'DynamicDataSourceType.AutoDetect' or 'DynamicDataSourceType.Field' (auto detect is the default when not specified explicitly, and is recommended) + Le membre « [DynamicData] » « {0}.{1} » est un champ, vous devez donc utiliser « DynamicDataSourceType.AutoDetect » ou « DynamicDataSourceType.Field » (la détection automatique est la valeur par défaut lorsqu’elle n’est pas spécifiée explicitement et est recommandée) + + '[DynamicData]' member '{0}.{1}' is a method so you should use 'DynamicDataSourceType.AutoDetect' or 'DynamicDataSourceType.Method' (auto detect is the default when not specified explicitly, and is recommended) '[DynamicData]' membre ' {0}.{1}' est une méthode, vous devez donc utiliser 'DynamicDataSourceType.AutoDetect' ou 'DynamicDataSourceType.Method' (la détection automatique est la valeur par défaut quand elle n’est pas spécifiée explicitement et est recommandée) + + '[DynamicData]' member '{0}.{1}' is not a property, method, or field. Only properties, methods, and fields are supported. + Le membre « [Données dynamiques] » « {0}.{1} » n’est pas une propriété, une méthode ou un champ. Seules les propriétés, les méthodes et les champs sont pris en charge. + + '[DynamicData]' member '{0}.{1}' is not a property nor a method. Only properties and methods are supported. '[DynamicData]' membre '{0}.{1}' n’est ni une propriété ni une méthode. Seules les propriétés et les méthodes sont prises en charge. @@ -427,6 +462,81 @@ Le type doit être une classe DynamicData doit être valide + + When calling async methods that have overloads accepting a CancellationToken parameter, prefer using the overload with TestContext.CancellationToken to enable cooperative cancellation and respect test timeouts. + Lors de l’appel de méthodes asynchrones qui ont des surcharges acceptant un paramètre CancellationToken, préférez utiliser la surcharge avec TestContext.CancellationToken pour activer l’annulation collaborative et respecter les délais d’expiration des tests. + + + + Consider using the overload that accepts a CancellationToken and pass 'TestContext.CancellationToken' + Envisagez d’utiliser la surcharge qui accepte un CancellationToken et passe « TestContext.CancellationToken » + + + + Flow TestContext.CancellationToken to async operations + Transmettez TestContext.CancellationToken aux opérations asynchrones + + + + Methods marked with '[GlobalTestInitialize]' or '[GlobalTestCleanup]' should follow the following layout to be valid: +-it can't be declared on a generic class +-it should be 'public' +-it should be 'static' +-it should not be 'async void' +-it should not be a special method (finalizer, operator...). +-it should not be generic +-it should take one parameter of type 'TestContext' +-return type should be 'void', 'Task' or 'ValueTask' + +The type declaring these methods should also respect the following rules: +-The type should be a class +-The class should be 'public' +-The class shouldn't be 'static' +-The class should be marked with '[TestClass]' (or a derived attribute) +-the class should not be generic. + Les méthodes marquées par « [GlobalTestInitialize] » ou « [GlobalTestCleanup] » doivent respecter le schéma suivant pour être valides : +- elle ne peut pas être déclarée dans une classe générique +– elle doit être « public » +– elle doit être « static » +– elle ne doit pas être « async void » +– il ne doit pas s’agir d’une méthode spéciale (finaliseur, opérateur...). +– elle ne doit pas être générique +– il doit prendre un paramètre de type « TestContext » +- le type de retour doit être « void », « Task » ou « ValueTask » + +Le type déclarant ces méthodes doit également respecter les règles suivantes : +- le type doit être une classe +- la classe doit être « public » +- la classe ne doit pas être « static » +- la classe doit être marquée par « [TestClass] » (ou un attribut dérivé) +- la classe ne doit pas être générique. + + + + Global test fixture method '{0}' signature is invalid + La signature de la méthode « {0} » de la fixture de test globale est invalide + + + + GlobalTestInitialize and GlobalTestCleanup methods should have valid layout + Les méthodes GlobalTestInitialize et GlobalTestCleanup doivent avoir une structure valide + + + + Methods like Contains, StartsWith, and EndsWith return boolean values that indicate whether the condition was met. Ignoring these return values is likely a mistake. + Les méthodes telles que Contains, StartsWith et EndsWith retournent des valeurs booléennes qui indiquent si la condition a été remplie. Ignorer ces valeurs de retour est certainement une erreur. + + + + The return value of '{0}' should not be ignored + La valeur de retour « {0} » ne doit pas être ignorée + + + + Do not ignore the return value of string methods + Ne négligez pas la valeur de retour des méthodes de chaîne + + Use 'Assert.Fail' instead of an always-failing 'Assert.{0}' assert Utilisez « Assert.Fail » à la place d’une assertion « Assert.{0} » toujours en échec @@ -437,6 +547,21 @@ Le type doit être une classe Utilisez « Assert.Fail » à la place d’une assertion toujours en échec + + 'DataTestMethodAttribute' is obsolete and provides no additional functionality over 'TestMethodAttribute'. Use 'TestMethodAttribute' for all test methods, including parameterized tests. + « DataTestMethodAttribute » est obsolète et ne fournit aucune fonctionnalité supplémentaire par rapport à « TestMethodAttribute ». Utiliser « TestMethodAttribute » pour toutes les méthodes de test, y compris les tests paramétrés. + + + + 'DataTestMethod' is obsolete. Use 'TestMethod' instead. + « DataTestMethod » est obsolète. Utilisez « TestMethod » à la place. + + + + Prefer 'TestMethod' over 'DataTestMethod' + Préférer « TestMethod » à « DataTestMethod » + + Review or remove the assertion as its condition is known to be always true Vérifier ou supprimer l’assertion, car sa condition est connue pour être toujours vraie @@ -517,6 +642,16 @@ Le type doit être une classe Les types publics doivent être des classes de test + + Use 'Assert.{0}' instead of 'StringAssert.{1}' + Utilisez « Assert.{0} » au lieu de « StringAssert.{1} » + + + + Use 'Assert' instead of 'StringAssert' + Utilisez « Assert » au lieu de « StringAssert » + + Test class '{0}' should be valid La classe de test « {0} » doit être valide @@ -532,6 +667,21 @@ Le type doit être une classe La signature « {0} » de la méthode de test n’est pas valide + + TestContext.CancellationToken provides a more direct way to access the cancellation token compared to TestContext.CancellationTokenSource.Token. + TestContext.CancellationToken offre un moyen plus direct d’accéder au jeton d’annulation par rapport à TestContext.CancellationTokenSource.Token. + + + + Use 'TestContext.CancellationToken' instead of 'TestContext.CancellationTokenSource.Token' + Utilisez « TestContext.CancellationToken » au lieu de « TestContext.CancellationTokenSource.Token » + + + + Use TestContext.CancellationToken instead of TestContext.CancellationTokenSource.Token + Utilisez TestContext.CancellationToken au lieu de TestContext.CancellationTokenSource.Token + + Without using 'ClassCleanupBehavior.EndOfClass', the '[ClassCleanup]' will by default be run at the end of the assembly and not at the end of the class. Sans utiliser 'ClassCleanupBehavior.EndOfClass', '[ClassCleanup]' sera exécuté par défaut à la fin de l’assemblée et non à la fin de la classe. @@ -720,13 +870,13 @@ Le type doit être une classe - Type contaning '[TestMethod]' should be marked with '[TestClass]', otherwise the test method will be silently ignored. + Type containing '[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 '{0}' contains test methods and should be marked with '[TestClass]' + Le type « {0} » contient des méthodes de test et doit être marquée avec « [TestClass] » @@ -744,6 +894,11 @@ Le type doit être une classe Les méthodes de test asynchrones ne nécessitent pas le suffixe 'Async' + + TestContext parameter is required by MSTest for AssemblyInitialize and ClassInitialize methods + Le paramètre TestContext est requis par MSTest pour les méthodes AssemblyInitialize et ClassInitialize + + [{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] @@ -764,6 +919,21 @@ Le type doit être une classe Utiliser 'ConditionBaseAttribute' sur les classes de test + + Using '[Timeout]' without explicitly setting 'CooperativeCancellation = true' is discouraged. In a future version, cooperative cancellation will become the default behavior. Set 'CooperativeCancellation = true' to opt into the recommended behavior and avoid breaking changes. + L’utilisation de '[Timeout]' sans définir explicitement 'CooperativeCancellation = true' est déconseillée. Dans une future version, l’annulation coopérative deviendra le comportement par défaut. Définissez 'CooperativeCancellation = true' pour adopter le comportement recommandé et éviter les changements incompatibles. + + + + Use 'CooperativeCancellation = true' with '[Timeout]' to enable cooperative cancellation behavior + Utilisez 'CooperativeCancellation = true' avec '[Timeout]' pour activer le comportement d’annulation coopératif + + + + Use 'CooperativeCancellation = true' with '[Timeout]' + Utiliser 'CooperativeCancellation = true' avec '[Timeout]' + + '[DeploymentItem]' can be specified only on test class or test method « [DeploymentItem] » ne peut être spécifié que sur une classe de test ou une méthode de test @@ -819,6 +989,36 @@ Le type doit être une classe Utilisez l’attribut de nouvelle tentative sur la méthode de test + + TestContext property cannot be accessed in this context + Impossible d’accéder à la propriété TestContext dans ce contexte + + + + TestContext property '{0}' cannot be accessed in '{1}' method + Impossible d’accéder à la propriété TestContext « {0} » dans la méthode « {1} » + + + + Some TestContext properties are only available during test execution and cannot be accessed in assembly initialize, class initialize, class cleanup, or assembly cleanup methods. + Certaines propriétés TestContext sont disponibles uniquement pendant l’exécution du test et ne sont pas accessibles dans les méthodes d’initialisation d’assembly, d’initialisation de classe, de nettoyage de classe ou de nettoyage d’assembly. + + + + Assert.Throws methods should contain only a single statement or expression. Multiple statements can be misleading - if the first statement throws, subsequent statements are never executed; if it doesn't throw, it should be moved outside the Assert.Throws. + Les méthodes Assert.Throws ne doivent contenir qu’une seule instruction ou expression. Plusieurs instructions peuvent prêter à confusion : si la première instruction lève une exception, les instructions suivantes ne sont jamais exécutées ; si elle ne lève pas d'exception, elle doit être déplacée en dehors de Assert.Throws. + + + + Assert.Throws should contain only a single statement/expression + Assert.Throws ne doit contenir qu’une seule instruction + + + + Assert.Throws should contain only a single statement/expression + Assert.Throws ne doit contenir qu’une seule instruction + + \ No newline at end of file diff --git a/src/Analyzers/MSTest.Analyzers/xlf/Resources.it.xlf b/src/Analyzers/MSTest.Analyzers/xlf/Resources.it.xlf index 3201a18d17..3c029d6838 100644 --- a/src/Analyzers/MSTest.Analyzers/xlf/Resources.it.xlf +++ b/src/Analyzers/MSTest.Analyzers/xlf/Resources.it.xlf @@ -132,6 +132,16 @@ Anche il tipo che dichiara questi metodi deve rispettare le regole seguenti: Non usare 'Assert.AreSame' o 'Assert.AreNotSame' con i tipi di valore + + Replace '{0}' with format parameters with string.Format or string interpolation + Sostituire '{0}' con i parametri di formato utilizzando string.Format o l'interpolazione di stringa + + + + Avoid using Assert methods with format parameters + Evitare di usare metodi Assert con parametri di formato + + Prefer 'Assert.ThrowsExactly' or 'Assert.ThrowsExactlyAsync' over '[ExpectedException]' as it ensures that only the expected call throws the expected exception. The assert APIs also provide more flexibility and allow you to assert extra properties of the exception. Preferire 'Assert.ThrowsExactly' o 'Assert.ThrowsExactlyAsync' a '[ExpectedException]' perché assicura che solo la chiamata prevista generi l'eccezione prevista. Le API di asserzione offrono anche maggiore flessibilità e consentono di dichiarare proprietà aggiuntive dell'eccezione. @@ -147,6 +157,16 @@ Anche il tipo che dichiara questi metodi deve rispettare le regole seguenti: Evita '[ExpectedException]' + + Remove the 'DynamicDataSourceType' argument to use the default auto detect behavior + Rimuovere l'argomento 'DynamicDataSourceType' per utilizzare il comportamento di rilevamento automatico predefinito + + + + Avoid passing an explicit 'DynamicDataSourceType' and use the default auto detect behavior + Evitare di passare l'argomento 'DynamicDataSourceType' in modo esplicito e utilizzare il comportamento di rilevamento automatico predefinito + + Do not assert inside 'async void' methods, local functions, or lambdas. Exceptions that are thrown in this context will be unhandled exceptions. When using VSTest under .NET Framework, they will be silently swallowed. When using Microsoft.Testing.Platform or VSTest under modern .NET, they may crash the process. Non dichiarare all'interno di metodi 'async void', funzioni locali o espressioni lambda. Le eccezioni generate in questo contesto saranno eccezioni non gestite. Quando si usa VSTest in .NET Framework, verranno eliminati automaticamente. Quando si usa Microsoft.Testing.Platform o VSTest nella versione moderna di .NET, è possibile che il processo venga arrestato in modo anomalo. @@ -277,8 +297,13 @@ Anche il tipo che dichiara questi metodi deve rispettare le regole seguenti: - DataRow argument type should match method parameter type. Mismatches occur at indices: {0} - Il tipo di argomento di DataRow deve corrispondere al tipo di parametro del metodo. Errori di corrispondenza in corrispondenza degli indici: {0} + DataRow argument types do not match method parameter types. {0} + Il tipo di argomento di DataRow deve corrispondere ai tipi di parametro del metodo. {0} + + + + Parameter '{0}' expects type '{1}', but the provided value has type '{2}' + Il parametro '{0}' prevede il tipo '{1}', ma il tipo del valore specificato è '{2}' @@ -402,11 +427,21 @@ Anche il tipo che dichiara questi metodi deve rispettare le regole seguenti: '[DynamicData]' deve essere impostato solo su un metodo di test + + '[DynamicData]' member '{0}.{1}' is a field so you should use 'DynamicDataSourceType.AutoDetect' or 'DynamicDataSourceType.Field' (auto detect is the default when not specified explicitly, and is recommended) + Il membro di '[DynamicData]' '{0}.{1}' è un campo, quindi è necessario usare 'DynamicDataSourceType.AutoDetect' o 'DynamicDataSourceType.Field' (il rilevamento automatico è l'impostazione predefinita quando non è specificato esplicitamente ed è l'opzione consigliata) + + '[DynamicData]' member '{0}.{1}' is a method so you should use 'DynamicDataSourceType.AutoDetect' or 'DynamicDataSourceType.Method' (auto detect is the default when not specified explicitly, and is recommended) '[DynamicData]' membro '{0}.{1}' è un metodo, quindi è consigliabile usare 'DynamicDataSourceType.AutoDetect' o 'DynamicDataSourceType.Method'. Il rilevamento automatico è l'impostazione predefinita quando non è specificata in modo esplicito ed è consigliabile + + '[DynamicData]' member '{0}.{1}' is not a property, method, or field. Only properties, methods, and fields are supported. + Il membro di '[DynamicData]' '{0}.{1}' non è una proprietà, un metodo o un campo. Sono supportati solo le proprietà, i metodi e i campi. + + '[DynamicData]' member '{0}.{1}' is not a property nor a method. Only properties and methods are supported. '[DynamicData]' membro '{0}.{1}' non è una proprietà né un metodo. Sono supportati solo metodi e proprietà. @@ -427,6 +462,81 @@ Anche il tipo che dichiara questi metodi deve rispettare le regole seguenti: DynamicData deve essere valido + + When calling async methods that have overloads accepting a CancellationToken parameter, prefer using the overload with TestContext.CancellationToken to enable cooperative cancellation and respect test timeouts. + Quando si chiamano metodi asincroni con overload che accettano un parametro CancellationToken, preferire l'uso dell'overload con TestContext.CancellationToken per abilitare l'annullamento cooperativo e rispettare i timeout di test. + + + + Consider using the overload that accepts a CancellationToken and pass 'TestContext.CancellationToken' + Valutare la possibilità di usare l'overload che accetta un CancellationToken e a passare 'TestContext.CancellationToken' + + + + Flow TestContext.CancellationToken to async operations + Flusso da TestContext.CancellationToken a operazioni asincrone + + + + Methods marked with '[GlobalTestInitialize]' or '[GlobalTestCleanup]' should follow the following layout to be valid: +-it can't be declared on a generic class +-it should be 'public' +-it should be 'static' +-it should not be 'async void' +-it should not be a special method (finalizer, operator...). +-it should not be generic +-it should take one parameter of type 'TestContext' +-return type should be 'void', 'Task' or 'ValueTask' + +The type declaring these methods should also respect the following rules: +-The type should be a class +-The class should be 'public' +-The class shouldn't be 'static' +-The class should be marked with '[TestClass]' (or a derived attribute) +-the class should not be generic. + I metodi contrassegnati con ‘[GlobalTestInitialize]’ o ‘[GlobalTestCleanup]’ devono seguire il layout seguente per essere validi: +- Non può essere dichiarato in una classe generica +- Deve essere 'public' +- Deve essere 'static' +- Non deve essere 'async void' +- Non deve essere un metodo speciale (finalizzatore, operatore...). +- Non deve essere generico +- Deve accettare un parametro di tipo 'TestContext' +- Il tipo restituito deve essere 'void', 'Task' o 'ValueTask' + +Anche il tipo che dichiara questi metodi deve rispettare le regole seguenti: +- Il tipo deve essere una classe +- La classe deve essere 'public' +- La classe non deve essere 'static' +- La classe deve essere contrassegnata con '[TestClass]' (o un attributo derivato) +- La classe non deve essere generica. + + + + Global test fixture method '{0}' signature is invalid + La firma del metodo di fixture di test globale '{0}' non è valida + + + + GlobalTestInitialize and GlobalTestCleanup methods should have valid layout + I metodi GlobalTestInitialize e GlobalTestCleanup devono avere un layout valido + + + + Methods like Contains, StartsWith, and EndsWith return boolean values that indicate whether the condition was met. Ignoring these return values is likely a mistake. + Metodi come Contains, StartsWith e EndsWith restituiscono valori booleani che indicano se la condizione è stata soddisfatta. Ignorare questi valori restituiti è probabilmente un errore. + + + + The return value of '{0}' should not be ignored + Il valore restituito di '{0}' non deve essere ignorato + + + + Do not ignore the return value of string methods + Non ignorare il valore restituito dei metodi stringa + + 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. @@ -437,6 +547,21 @@ Anche il tipo che dichiara questi metodi deve rispettare le regole seguenti: Usare 'Assert.Fail' invece di un'asserzione che ha sempre esito negativo + + 'DataTestMethodAttribute' is obsolete and provides no additional functionality over 'TestMethodAttribute'. Use 'TestMethodAttribute' for all test methods, including parameterized tests. + 'DataTestMethodAttribute' è obsoleto e non offre funzionalità aggiuntive rispetto a 'TestMethodAttribute'. Utilizzare 'TestMethodAttribute' per tutti i metodi di test, inclusi i test con parametri. + + + + 'DataTestMethod' is obsolete. Use 'TestMethod' instead. + 'DataTestMethod' è obsoleto. Utilizzare 'TestMethod' invece. + + + + Prefer 'TestMethod' over 'DataTestMethod' + Preferisci 'TestMethod' a 'DataTestMethod' + + Review or remove the assertion as its condition is known to be always true Rivedere o rimuovere l'asserzione perché la relativa condizione è sempre true @@ -517,6 +642,16 @@ Anche il tipo che dichiara questi metodi deve rispettare le regole seguenti: I tipi di pubblico dovrebbero essere classi di test + + Use 'Assert.{0}' instead of 'StringAssert.{1}' + Usa 'Assert.{0}' invece di 'StringAssert.{1}' + + + + Use 'Assert' instead of 'StringAssert' + Usa 'Assert' invece di 'StringAssert' + + Test class '{0}' should be valid La classe di test '{0}' deve essere valida @@ -532,6 +667,21 @@ Anche il tipo che dichiara questi metodi deve rispettare le regole seguenti: La firma del metodo di test '{0}' non è valida + + TestContext.CancellationToken provides a more direct way to access the cancellation token compared to TestContext.CancellationTokenSource.Token. + TestContext.CancellationToken offre un modo più diretto per accedere al token di annullamento rispetto a TestContext.CancellationTokenSource.Token. + + + + Use 'TestContext.CancellationToken' instead of 'TestContext.CancellationTokenSource.Token' + Usare 'TestContext.CancellationToken' invece di 'TestContext.CancellationTokenSource.Token' + + + + Use TestContext.CancellationToken instead of TestContext.CancellationTokenSource.Token + Usare TestContext.CancellationToken invece di TestContext.CancellationTokenSource.Token + + Without using 'ClassCleanupBehavior.EndOfClass', the '[ClassCleanup]' will by default be run at the end of the assembly and not at the end of the class. Se non si usa 'ClassCleanupBehavior.EndOfClass', '[ClassCleanup]' verrà eseguito per impostazione predefinita alla fine dell'assembly e non alla fine della classe. @@ -720,13 +870,13 @@ Anche il tipo che dichiara questi metodi deve rispettare le regole seguenti: - Type contaning '[TestMethod]' should be marked with '[TestClass]', otherwise the test method will be silently ignored. + Type containing '[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 '{0}' contains test methods and should be marked with '[TestClass]' + Il tipo '{0}' contiene metodi di test e deve essere contrassegnato come '[TestClass]' @@ -744,6 +894,11 @@ Anche il tipo che dichiara questi metodi deve rispettare le regole seguenti: I metodi di test asincroni non richiedono il suffisso 'Async' + + TestContext parameter is required by MSTest for AssemblyInitialize and ClassInitialize methods + Il parametro TestContext è necessario a MSTest per i metodi AssemblyInitialize e ClassInitialize + + [{0}] can only be set on methods marked with [TestMethod] [{0}] può essere impostato solo su metodi contrassegnati con [TestMethod] @@ -764,6 +919,21 @@ Anche il tipo che dichiara questi metodi deve rispettare le regole seguenti: Usa 'ConditionBaseAttribute' nelle classi di test + + Using '[Timeout]' without explicitly setting 'CooperativeCancellation = true' is discouraged. In a future version, cooperative cancellation will become the default behavior. Set 'CooperativeCancellation = true' to opt into the recommended behavior and avoid breaking changes. + L'uso di "[Timeout]" senza impostare esplicitamente "CooperativeCancellation = true" è sconsigliato. In una versione futura l'annullamento cooperativo diventerà il comportamento predefinito. Impostare "CooperativeCancellation = true" per acconsentire esplicitamente al comportamento consigliato ed evitare modifiche che causano un'interruzione. + + + + Use 'CooperativeCancellation = true' with '[Timeout]' to enable cooperative cancellation behavior + Usare "CooperativeCancellation = true" con "[Timeout]" per abilitare il comportamento di annullamento cooperativo + + + + Use 'CooperativeCancellation = true' with '[Timeout]' + Usa "CooperativeCancellation = true" con "[Timeout]" + + '[DeploymentItem]' can be specified only on test class or test method '[DeploymentItem]' può essere specificato solo per la classe di test o il metodo di test @@ -819,6 +989,36 @@ Anche il tipo che dichiara questi metodi deve rispettare le regole seguenti: Utilizzare l'attributo di ripetizione nel metodo di test + + TestContext property cannot be accessed in this context + Non è possibile accedere alla proprietà TestContext in questo contesto + + + + TestContext property '{0}' cannot be accessed in '{1}' method + Non è possibile accedere alla proprietà TestContext '{0}' nel metodo '{1}' + + + + Some TestContext properties are only available during test execution and cannot be accessed in assembly initialize, class initialize, class cleanup, or assembly cleanup methods. + Alcune proprietà TestContext sono disponibili solo durante l'esecuzione dei test e non è possibile accedervi nei metodi di inizializzazione degli assembly, inizializzazione delle classi, pulizia delle classi o pulizia degli assembly. + + + + Assert.Throws methods should contain only a single statement or expression. Multiple statements can be misleading - if the first statement throws, subsequent statements are never executed; if it doesn't throw, it should be moved outside the Assert.Throws. + I metodi Assert.Throws devono contenere solo una singola istruzione o espressione. Istruzioni multiple possono essere fuorvianti: se la prima istruzione genera, le istruzioni successive non vengono mai eseguite; se non genera, è necessario spostarla all'esterno di Assert.Throws. + + + + Assert.Throws should contain only a single statement/expression + Assert.Throws deve contenere solo una singola istruzione/espressione + + + + Assert.Throws should contain only a single statement/expression + Assert.Throws deve contenere solo una singola istruzione/espressione + + \ No newline at end of file diff --git a/src/Analyzers/MSTest.Analyzers/xlf/Resources.ja.xlf b/src/Analyzers/MSTest.Analyzers/xlf/Resources.ja.xlf index 695f062ace..5d80076bd1 100644 --- a/src/Analyzers/MSTest.Analyzers/xlf/Resources.ja.xlf +++ b/src/Analyzers/MSTest.Analyzers/xlf/Resources.ja.xlf @@ -132,6 +132,16 @@ The type declaring these methods should also respect the following rules: 値型に 'Assert.AreSame' または 'Assert.AreNotSame' を使用しないでください + + Replace '{0}' with format parameters with string.Format or string interpolation + '{0}' を string.Format または文字列の補間で書式設定パラメーターに置き換える + + + + Avoid using Assert methods with format parameters + 書式設定パラメーターで Assert メソッドを使用しないようにする + + Prefer 'Assert.ThrowsExactly' or 'Assert.ThrowsExactlyAsync' over '[ExpectedException]' as it ensures that only the expected call throws the expected exception. The assert APIs also provide more flexibility and allow you to assert extra properties of the exception. '[ExpectedException]' よりも 'Assert.ThrowsExactly' または 'Assert.ThrowsExactlyAsync' を優先します。これは、予期された呼び出しのみが予期された例外をスローするようにするためです。アサート API もさらに柔軟性が高く、これにより例外の追加プロパティをアサートできます。 @@ -147,6 +157,16 @@ The type declaring these methods should also respect the following rules: '[ExpectedException]' を回避する + + Remove the 'DynamicDataSourceType' argument to use the default auto detect behavior + 既定の自動検出動作を使用するには、'DynamicDataSourceType' 引数を削除してください + + + + Avoid passing an explicit 'DynamicDataSourceType' and use the default auto detect behavior + 明示的な 'DynamicDataSourceType' を渡さず、既定の自動検出動作を使用してください + + Do not assert inside 'async void' methods, local functions, or lambdas. Exceptions that are thrown in this context will be unhandled exceptions. When using VSTest under .NET Framework, they will be silently swallowed. When using Microsoft.Testing.Platform or VSTest under modern .NET, they may crash the process. 'async void' メソッド、ローカル関数、またはラムダ内ではアサートしないでください。このコンテキストでスローされる例外は、ハンドルされない例外になります。.NET Frameworkで VSTest を使用すると、警告なしに飲み込まれるようになります。最新の .NET で Microsoft.Testing.Platform または VSTest を使用すると、プロセスがクラッシュする可能性があります。 @@ -277,8 +297,13 @@ The type declaring these methods should also respect the following rules: - DataRow argument type should match method parameter type. Mismatches occur at indices: {0} - DataRow 引数の型は、メソッド パラメーターの型と一致させる必要があります。インデックスで不一致が発生しました: {0} + DataRow argument types do not match method parameter types. {0} + DataRow 引数の型がメソッド パラメーターの型と一致しません。 {0} + + + + Parameter '{0}' expects type '{1}', but the provided value has type '{2}' + パラメーター '{0}' の型は '{1}' が想定されていますが、指定された値の型は '{2}' です @@ -402,11 +427,21 @@ The type declaring these methods should also respect the following rules: '[DynamicData]' はテスト メソッドでのみ設定する必要があります + + '[DynamicData]' member '{0}.{1}' is a field so you should use 'DynamicDataSourceType.AutoDetect' or 'DynamicDataSourceType.Field' (auto detect is the default when not specified explicitly, and is recommended) + '[DynamicData]' のメンバー '{0}.{1}' はフィールドであるため、'DynamicDataSourceType.AutoDetect' または 'DynamicDataSourceType.Field' を使用する必要があります (明示的に指定しない場合は既定で自動検出され、これが推奨されます) + + '[DynamicData]' member '{0}.{1}' is a method so you should use 'DynamicDataSourceType.AutoDetect' or 'DynamicDataSourceType.Method' (auto detect is the default when not specified explicitly, and is recommended) '[DynamicData]' メンバー '{0}.{1}' はメソッドであるため、'DynamicDataSourceType.AutoDetect' または 'DynamicDataSourceType.Method' を使用する必要があります (自動検出は明示的に指定されていない場合の既定値であり、推奨されます) + + '[DynamicData]' member '{0}.{1}' is not a property, method, or field. Only properties, methods, and fields are supported. + '[DynamicData]' のメンバー '{0}.{1}' は、プロパティ、メソッド、フィールドではありません。サポートされているのは、プロパティ、メソッド、フィールドのみです。 + + '[DynamicData]' member '{0}.{1}' is not a property nor a method. Only properties and methods are supported. メンバー '{0}.{1}' '[DynamicData]' プロパティでもメソッドでもありません。プロパティとメソッドのみがサポートされています。 @@ -427,6 +462,81 @@ The type declaring these methods should also respect the following rules: DynamicData は有効である必要があります + + When calling async methods that have overloads accepting a CancellationToken parameter, prefer using the overload with TestContext.CancellationToken to enable cooperative cancellation and respect test timeouts. + CancellationToken パラメーターを受け入れるオーバーロードを持つ非同期メソッドを呼び出す場合は、TestContext.CancellationToken でオーバーロードを使用して協調的な取り消しを有効にし、テスト タイムアウトを考慮します。 + + + + Consider using the overload that accepts a CancellationToken and pass 'TestContext.CancellationToken' + CancellationToken を受け取り、'TestContext.CancellationToken' を渡すオーバーロードの使用を検討してください + + + + Flow TestContext.CancellationToken to async operations + TestContext.CancellationToken を非同期操作にフローする + + + + Methods marked with '[GlobalTestInitialize]' or '[GlobalTestCleanup]' should follow the following layout to be valid: +-it can't be declared on a generic class +-it should be 'public' +-it should be 'static' +-it should not be 'async void' +-it should not be a special method (finalizer, operator...). +-it should not be generic +-it should take one parameter of type 'TestContext' +-return type should be 'void', 'Task' or 'ValueTask' + +The type declaring these methods should also respect the following rules: +-The type should be a class +-The class should be 'public' +-The class shouldn't be 'static' +-The class should be marked with '[TestClass]' (or a derived attribute) +-the class should not be generic. + '[GlobalTestInitialize]' または '[GlobalTestCleanup]' でマークされたメソッドを有効にするには、次のレイアウトに従う必要があります: +- ジェネリック クラスで宣言することはできません +- 'public' である必要があります +- 'static' である必要があります +- 'async void' にすることはできません +- 特殊なメソッド (ファイナライザー、演算子...) にすることはできません。 +- ジェネリックにすることはできません +- 'TestContext' 型のパラメーターを 1 つ受け取る必要があります +- 戻り値の型が 'void'、'Task'、または 'ValueTask' である必要があります + +これらのメソッドを宣言する型も、次の規則に従う必要があります: +- 型はクラスである必要があります +-クラスは 'public' である必要があります +- クラスを 'static' にすることはできません +- クラスは '[TestClass]' (または派生属性) でマークする必要があります +- クラスをジェネリックにすることはできません。 + + + + Global test fixture method '{0}' signature is invalid + グローバル テスト フィクスチャ メソッド '{0}' シグネチャが無効です + + + + GlobalTestInitialize and GlobalTestCleanup methods should have valid layout + GlobalTestInitialize および GlobalTestCleanup メソッドには有効なレイアウトが必要です + + + + Methods like Contains, StartsWith, and EndsWith return boolean values that indicate whether the condition was met. Ignoring these return values is likely a mistake. + Contains、StartsWith、EndsWith などのメソッドは、条件が満たされたかどうかを示すブール値を返します。これらの戻り値を無視するのは間違いである可能性が高いです。 + + + + The return value of '{0}' should not be ignored + '{0}' の戻り値は無視しないでください + + + + Do not ignore the return value of string methods + 文字列メソッドの戻り値を無視しない + + Use 'Assert.Fail' instead of an always-failing 'Assert.{0}' assert 常に失敗している 'Assert.{0}' アサートの代わりに 'Assert.Fail' を使用する。 @@ -437,6 +547,21 @@ The type declaring these methods should also respect the following rules: 常に失敗しているアサートの代わりに 'Assert.Fail' を使用する + + 'DataTestMethodAttribute' is obsolete and provides no additional functionality over 'TestMethodAttribute'. Use 'TestMethodAttribute' for all test methods, including parameterized tests. + 'DataTestMethodAttribute' は廃止されており、'TestMethodAttribute' に対して追加の機能は提供しません。パラメーター化されたテストを含むすべてのテスト メソッドには、'TestMethodAttribute' を使用してください。 + + + + 'DataTestMethod' is obsolete. Use 'TestMethod' instead. + 'DataTestMethod' は廃止されました。代わりに 'TestMethod' を使用してください。 + + + + Prefer 'TestMethod' over 'DataTestMethod' + 'DataTestMethod' よりも 'TestMethod' を優先する + + Review or remove the assertion as its condition is known to be always true アサーションの条件が常に TRUE であることが判明しているため、アサーションを確認または削除してください @@ -517,6 +642,16 @@ The type declaring these methods should also respect the following rules: パブリック型はテスト クラスである必要があります + + Use 'Assert.{0}' instead of 'StringAssert.{1}' + 'StringAssert.{1}' の代わりに 'Assert.{0}' を使用します + + + + Use 'Assert' instead of 'StringAssert' + 'StringAssert' の代わりに 'Assert' を使用 + + Test class '{0}' should be valid テスト クラス '{0}' は有効である必要があります @@ -532,6 +667,21 @@ The type declaring these methods should also respect the following rules: テスト メソッド '{0}' シグネチャが無効です + + TestContext.CancellationToken provides a more direct way to access the cancellation token compared to TestContext.CancellationTokenSource.Token. + TestContext.CancellationToken は、TestContext.CancellationTokenSource.Token と比較して、より直接的に取り消しトークンにアクセスできます。 + + + + Use 'TestContext.CancellationToken' instead of 'TestContext.CancellationTokenSource.Token' + 'TestContext.CancellationTokenSource.Token' の代わりに 'TestContext.CancellationToken' を使用する + + + + Use TestContext.CancellationToken instead of TestContext.CancellationTokenSource.Token + TestContext.CancellationTokenSource.Token の代わりに TestContext.CancellationToken を使用する + + Without using 'ClassCleanupBehavior.EndOfClass', the '[ClassCleanup]' will by default be run at the end of the assembly and not at the end of the class. 'ClassCleanupBehavior.EndOfClass' を使用しない場合、'[ClassCleanup]' は既定でアセンブリの最後に実行され、クラスの最後には実行されません。 @@ -720,13 +870,13 @@ The type declaring these methods should also respect the following rules: - Type contaning '[TestMethod]' should be marked with '[TestClass]', otherwise the test method will be silently ignored. - 連続する型 '[TestMethod]' は '[TestClass]' でマークする必要があります。それ以外の場合、テスト メソッドは暗黙的に無視されます。 + Type containing '[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 '{0}' contains test methods and should be marked with '[TestClass]' + 型 '{0}' にはテスト メソッドが含まれており、'[TestClass]' でマークする必要があります @@ -744,6 +894,11 @@ The type declaring these methods should also respect the following rules: 非同期テスト メソッドには 'Async' サフィックスは不要です + + TestContext parameter is required by MSTest for AssemblyInitialize and ClassInitialize methods + AssemblyInitialize メソッドと ClassInitialize メソッドに対する MSTest には TestContext パラメーターが必要です + + [{0}] can only be set on methods marked with [TestMethod] [{0}] は、[TestMethod] でマークされたメソッドにのみ設定できます @@ -764,6 +919,21 @@ The type declaring these methods should also respect the following rules: テスト クラスで 'ConditionBaseAttribute' を使用する + + Using '[Timeout]' without explicitly setting 'CooperativeCancellation = true' is discouraged. In a future version, cooperative cancellation will become the default behavior. Set 'CooperativeCancellation = true' to opt into the recommended behavior and avoid breaking changes. + 'CooperativeCancellation = true' を明示的にを設定せずに '[Timeout]' を使用することは推奨されません。将来のバージョンでは、協調的キャンセルが既定の動作になります。推奨される動作をオプトインし、破壊的な変更を避けるために、'CooperativeCancellation = true' を設定します。 + + + + Use 'CooperativeCancellation = true' with '[Timeout]' to enable cooperative cancellation behavior + '[Timeout]' と共に 'CooperativeCancellation = true' を使用して、協調的キャンセルの動作を有効にします + + + + Use 'CooperativeCancellation = true' with '[Timeout]' + '[Timeout]' と共に 'CooperativeCancellation = true' を使用する + + '[DeploymentItem]' can be specified only on test class or test method '[DeploymentItem]' は、テスト クラスまたはテスト メソッドでのみ指定できます @@ -819,6 +989,36 @@ The type declaring these methods should also respect the following rules: テスト メソッドで retry 属性を使用する + + TestContext property cannot be accessed in this context + このコンテキストでは TestContext プロパティにアクセスできません + + + + TestContext property '{0}' cannot be accessed in '{1}' method + TestContext プロパティ '{0}' に '{1}' メソッドでアクセスできません + + + + Some TestContext properties are only available during test execution and cannot be accessed in assembly initialize, class initialize, class cleanup, or assembly cleanup methods. + TestContext プロパティの一部はテストの実行中にのみ使用でき、assenbly initialize、class initialize、class cleanup、または assembly cleanup メソッドではアクセスできません。 + + + + Assert.Throws methods should contain only a single statement or expression. Multiple statements can be misleading - if the first statement throws, subsequent statements are never executed; if it doesn't throw, it should be moved outside the Assert.Throws. + Assert.Throws メソッドには 1 つのステートメントまたは式のみを含める必要があります。複数のステートメントは誤解を招く可能性があります。最初のステートメントがスローされた場合、後続のステートメントは実行されません。スローされない場合は、Assert.Throws の外部に移動する必要があります。 + + + + Assert.Throws should contain only a single statement/expression + Assert.Throws には 1 つのステートメントまたは式のみを含める必要があります + + + + Assert.Throws should contain only a single statement/expression + Assert.Throws には 1 つのステートメントまたは式のみを含める必要があります + + \ No newline at end of file diff --git a/src/Analyzers/MSTest.Analyzers/xlf/Resources.ko.xlf b/src/Analyzers/MSTest.Analyzers/xlf/Resources.ko.xlf index 1f862f2ac8..0a46f0e065 100644 --- a/src/Analyzers/MSTest.Analyzers/xlf/Resources.ko.xlf +++ b/src/Analyzers/MSTest.Analyzers/xlf/Resources.ko.xlf @@ -132,6 +132,16 @@ The type declaring these methods should also respect the following rules: 값 형식에 'Assert.AreSame' 또는 'Assert.AreNotSame'을 사용하지 마세요. + + Replace '{0}' with format parameters with string.Format or string interpolation + '{0}'을(를) string.Format이나 문자열 보간을 사용하는 포맷 매개변수로 변경하기 + + + + Avoid using Assert methods with format parameters + 형식 매개 변수를 사용하여 Assert 메서드는 사용하지 마세요 + + Prefer 'Assert.ThrowsExactly' or 'Assert.ThrowsExactlyAsync' over '[ExpectedException]' as it ensures that only the expected call throws the expected exception. The assert APIs also provide more flexibility and allow you to assert extra properties of the exception. 예상되는 호출만 예상되는 예외를 던지도록 하기 위해 '[ExpectedException]보다 'Assert.ThrowsExactly' 또는 'Assert.ThrowsExactlyAsync'를 사용하는 것이 좋습니다. 또한 어설션 API는 더 많은 유연성을 제공하고 예외의 추가 속성을 어설션할 수 있습니다. @@ -147,6 +157,16 @@ The type declaring these methods should also respect the following rules: '[ExpectedException]' 사용 지양 + + Remove the 'DynamicDataSourceType' argument to use the default auto detect behavior + 기본 자동 검색 동작을 사용하려면 'DynamicDataSourceType' 인수를 제거합니다. + + + + Avoid passing an explicit 'DynamicDataSourceType' and use the default auto detect behavior + 명시적 'DynamicDataSourceType'을 전달하지 말고 기본 자동 검색 동작을 사용합니다. + + Do not assert inside 'async void' methods, local functions, or lambdas. Exceptions that are thrown in this context will be unhandled exceptions. When using VSTest under .NET Framework, they will be silently swallowed. When using Microsoft.Testing.Platform or VSTest under modern .NET, they may crash the process. 'async void' 메서드, 로컬 함수 또는 람다 내에서 어설션하지 마십시오. 이 컨텍스트에서 throw된 예외는 처리되지 않은 예외가 됩니다. .NET Framework 아래에서 VSTest를 사용하면 자동으로 무시됩니다. 최신 .NET에서 Microsoft.Testing.Platform 또는 VSTest를 사용하는 경우 프로세스가 중단될 수 있습니다. @@ -277,8 +297,13 @@ The type declaring these methods should also respect the following rules: - DataRow argument type should match method parameter type. Mismatches occur at indices: {0} - DataRow 인수 형식은 메서드 매개 변수 형식과 일치해야 합니다. 인덱스에서 불일치 발생: {0} + DataRow argument types do not match method parameter types. {0} + DataRow 인수 형식이 메서드 매개 변수 형식과 일치하지 않습니다. {0} + + + + Parameter '{0}' expects type '{1}', but the provided value has type '{2}' + '{0}' 매개 변수에는 '{1}' 형식이 필요한데 제공된 값의 형식은 '{2}'입니다. @@ -402,11 +427,21 @@ The type declaring these methods should also respect the following rules: '[DynamicData]'는 테스트 메서드에서만 설정해야 합니다. + + '[DynamicData]' member '{0}.{1}' is a field so you should use 'DynamicDataSourceType.AutoDetect' or 'DynamicDataSourceType.Field' (auto detect is the default when not specified explicitly, and is recommended) + '[DynamicData]' 구성원 '{0}.{1}'은(는) 필드이므로 'DynamicDataSourceType.AutoDetect' 또는 'DynamicDataSourceType.Field'를 사용해야 합니다(명시적으로 지정하지 않은 경우 자동 검색이 기본값이며 권장됨). + + '[DynamicData]' member '{0}.{1}' is a method so you should use 'DynamicDataSourceType.AutoDetect' or 'DynamicDataSourceType.Method' (auto detect is the default when not specified explicitly, and is recommended) '[DynamicData]' 멤버 '{0}.{1}'은(는) 'DynamicDataSourceType.AutoDetect' 또는 'DynamicDataSourceType.Method'를 사용해야 하는 메서드입니다.(명시적으로 지정하지 않은 경우 자동 검색이 기본값이며 권장) + + '[DynamicData]' member '{0}.{1}' is not a property, method, or field. Only properties, methods, and fields are supported. + '[DynamicData]' 구성원 '{0}.{1}'은(는) 속성, 메서드 또는 필드가 아닙니다. 속성, 메서드 및 필드만 지원됩니다. + + '[DynamicData]' member '{0}.{1}' is not a property nor a method. Only properties and methods are supported. '[DynamicData]' 멤버 '{0}.{1}'은(는) 속성이나 메서드가 아닙니다. 속성 및 메서드만 지원됩니다. @@ -427,6 +462,81 @@ The type declaring these methods should also respect the following rules: DynamicData는 유효해야 합니다. + + When calling async methods that have overloads accepting a CancellationToken parameter, prefer using the overload with TestContext.CancellationToken to enable cooperative cancellation and respect test timeouts. + CancellationToken 매개변수를 허용하는 오버로드가 있는 비동기 메서드를 호출할 때는 협력적 취소를 활성화하고 테스트 시간 제한을 준수하기 위해 TestContext.CancellationToken이 있는 오버로드를 사용하는 것이 좋습니다. + + + + Consider using the overload that accepts a CancellationToken and pass 'TestContext.CancellationToken' + CancellationToken을 수락하고 'TestContext.CancellationToken'을 전달하는 오버로드를 사용하는 것이 좋습니다. + + + + Flow TestContext.CancellationToken to async operations + 비동기 작업으로 TestContext.CancellationToken 흐름 + + + + Methods marked with '[GlobalTestInitialize]' or '[GlobalTestCleanup]' should follow the following layout to be valid: +-it can't be declared on a generic class +-it should be 'public' +-it should be 'static' +-it should not be 'async void' +-it should not be a special method (finalizer, operator...). +-it should not be generic +-it should take one parameter of type 'TestContext' +-return type should be 'void', 'Task' or 'ValueTask' + +The type declaring these methods should also respect the following rules: +-The type should be a class +-The class should be 'public' +-The class shouldn't be 'static' +-The class should be marked with '[TestClass]' (or a derived attribute) +-the class should not be generic. + '[GlobalTestInitialize]' 또는 '[GlobalTestCleanup]'으로 표시된 메서드는 다음 레이아웃을 따라야 유효합니다. +- 제네릭 클래스에서 선언할 수 없습니다. +- 'public'이어야 합니다. +- 'static'이어야 합니다. +- 'async void'가 아니어야 합니다. +- 특수 메서드(종료자, 연산자...)가 아니어야 합니다. +- 제네릭이 아니어야 합니다. +- 'TestContext' 형식의 매개 변수를 하나 사용해야 합니다. +- 반환 형식은 'void', 'Task' 또는 'ValueTask'여야 합니다. + +이러한 메서드를 선언하는 형식은 다음 규칙도 준수해야 합니다. +- 형식은 클래스여야 합니다. +-클래스는 'public'이어야 합니다. +- 클래스는 'static'이 되어서는 안 됩니다. +- 클래스는 '[TestClass]'(또는 파생 특성)로 표시되어야 합니다. +- 클래스는 제네릭이 아니어야 합니다. + + + + Global test fixture method '{0}' signature is invalid + 전역 테스트 fixture 메서드 '{0}' 시그니처가 잘못되었습니다. + + + + GlobalTestInitialize and GlobalTestCleanup methods should have valid layout + GlobalTestInitialize 및 GlobalTestCleanup 메서드에는 유효한 레이아웃이 있어야 합니다. + + + + Methods like Contains, StartsWith, and EndsWith return boolean values that indicate whether the condition was met. Ignoring these return values is likely a mistake. + Contains, StartsWith 및 EndsWith와 같은 메서드는 조건이 충족되었는지 여부를 나타내는 부울 값을 반환합니다. 이 반환 값을 무시하는 것은 아마 실수일 것입니다. + + + + The return value of '{0}' should not be ignored + '{0}'의 반환 값은 무시하면 안 됩니다. + + + + Do not ignore the return value of string methods + 문자열 메서드의 반환 값을 무시하지 마세요. + + Use 'Assert.Fail' instead of an always-failing 'Assert.{0}' assert 항상 실패하는 'Assert.{0}' 어설션 대신 'Assert.Fail'을 사용합니다. @@ -437,6 +547,21 @@ The type declaring these methods should also respect the following rules: 항상 실패하는 어설션 대신 'Assert.Fail' 사용 + + 'DataTestMethodAttribute' is obsolete and provides no additional functionality over 'TestMethodAttribute'. Use 'TestMethodAttribute' for all test methods, including parameterized tests. + 'DataTestMethodAttribute'는 더 이상 사용되지 않으며 'TestMethodAttribute'에 대한 추가 기능을 제공하지 않습니다. 매개 변수가 있는 테스트를 포함하여 모든 테스트 메서드에 'TestMethodAttribute'를 사용하세요. + + + + 'DataTestMethod' is obsolete. Use 'TestMethod' instead. + 'DataTestMethod'는 사용되지 않습니다. 대신 'TestMethod'를 사용하세요. + + + + Prefer 'TestMethod' over 'DataTestMethod' + 'DataTestMethod'보다 'TestMethod' 선호 + + Review or remove the assertion as its condition is known to be always true 조건이 항상 true인 것으로 알려진 어설션 검토 또는 제거 @@ -517,6 +642,16 @@ The type declaring these methods should also respect the following rules: 공용 형식은 테스트 클래스여야 합니다. + + Use 'Assert.{0}' instead of 'StringAssert.{1}' + 'StringAssert.{1}' 대신 'Assert.{0}' 사용 + + + + Use 'Assert' instead of 'StringAssert' + 'StringAssert' 대신 'Assert' 사용 + + Test class '{0}' should be valid 테스트 클래스 '{0}'은(는) 유효해야 합니다. @@ -532,6 +667,21 @@ The type declaring these methods should also respect the following rules: 테스트 메서드 '{0}' 시그니처가 잘못되었습니다. + + TestContext.CancellationToken provides a more direct way to access the cancellation token compared to TestContext.CancellationTokenSource.Token. + TestContext.CancellationToken은 TestContext.CancellationTokenSource.Token보다 취소 토큰에 더 직접적으로 액세스할 수 있는 방법을 제공합니다. + + + + Use 'TestContext.CancellationToken' instead of 'TestContext.CancellationTokenSource.Token' + 'TestContext.CancellationTokenSource.Token' 대신 'TestContext.CancellationToken' 사용 + + + + Use TestContext.CancellationToken instead of TestContext.CancellationTokenSource.Token + TestContext.CancellationTokenSource.Token 대신 TestContext.CancellationToken 사용 + + Without using 'ClassCleanupBehavior.EndOfClass', the '[ClassCleanup]' will by default be run at the end of the assembly and not at the end of the class. 'ClassCleanupBehavior.EndOfClass'를 사용하지 않으면 '[ClassCleanup]'은 기본적으로 클래스의 끝이 아니라 어셈블리의 끝에서 실행됩니다. @@ -720,13 +870,13 @@ The type declaring these methods should also respect the following rules: - Type contaning '[TestMethod]' should be marked with '[TestClass]', otherwise the test method will be silently ignored. + Type containing '[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 '{0}' contains test methods and should be marked with '[TestClass]' + '{0}' 유형에는 테스트 메서드가 포함되어 있으며 '[TestClass]'로 표시되어야 합니다. @@ -744,6 +894,11 @@ The type declaring these methods should also respect the following rules: 비동기 테스트 메서드에는 'Async' 접미사가 필요하지 않습니다. + + TestContext parameter is required by MSTest for AssemblyInitialize and ClassInitialize methods + AssemblyInitialize 및 ClassInitialize 메서드에 대해 MSTest에 의한 TestContext 매개 변수가 필요합니다. + + [{0}] can only be set on methods marked with [TestMethod] [{0}]은(는) [TestMethod] 표시된 메서드에만 설정할 수 있습니다. @@ -764,6 +919,21 @@ The type declaring these methods should also respect the following rules: 테스트 클래스에 'ConditionBaseAttribute' 사용 + + Using '[Timeout]' without explicitly setting 'CooperativeCancellation = true' is discouraged. In a future version, cooperative cancellation will become the default behavior. Set 'CooperativeCancellation = true' to opt into the recommended behavior and avoid breaking changes. + 'CooperativeCancellation = true'를 명시적으로 설정하지 않고 '[Timeout]'을 사용하는 것은 권장되지 않습니다. 향후 버전에서는 협동 취소가 기본 동작으로 설정될 것입니다. 권장 동작을 선택하고 변경 사항으로 인한 문제를 피하려면 'CooperativeCancellation = true'를 설정하세요. + + + + Use 'CooperativeCancellation = true' with '[Timeout]' to enable cooperative cancellation behavior + '[Timeout]'와 함께 'CooperativeCancellation = true'를 사용하여 협동 취소 동작을 활성화하세요. + + + + Use 'CooperativeCancellation = true' with '[Timeout]' + '[Timeout]'와 함께 'CooperativeCancellation = true'를 사용하세요. + + '[DeploymentItem]' can be specified only on test class or test method '[DeploymentItem]'은(는) 테스트 클래스 또는 테스트 메서드에만 지정할 수 있습니다. @@ -819,6 +989,36 @@ The type declaring these methods should also respect the following rules: 테스트 메서드에 재시도 속성 사용 + + TestContext property cannot be accessed in this context + 이 컨텍스트에서 TestContext 속성에 액세스할 수 없습니다. + + + + TestContext property '{0}' cannot be accessed in '{1}' method + '{1}' 메서드에서 TestContext 속성 '{0}'에 액세스할 수 없습니다. + + + + Some TestContext properties are only available during test execution and cannot be accessed in assembly initialize, class initialize, class cleanup, or assembly cleanup methods. + 일부 TestContext 속성은 테스트 실행 중에만 사용할 수 있으며, 어셈블리 초기화, 클래스 초기화, 클래스 정리 또는 어셈블리 정리 메서드에서는 액세스할 수 없습니다. + + + + Assert.Throws methods should contain only a single statement or expression. Multiple statements can be misleading - if the first statement throws, subsequent statements are never executed; if it doesn't throw, it should be moved outside the Assert.Throws. + Assert.Throws 메서드는 단일 문 또는 식만 포함해야 합니다. 여러 문이 있을 경우 혼란을 줄 수 있습니다. 첫 번째 문이 예외를 발생시키면 후속 문은 실행되지 않으며, 예외가 발생하지 않는 경우에는 Assert.Throws 외부로 이동해야 합니다. + + + + Assert.Throws should contain only a single statement/expression + Assert.Throws는 단일 문/식만 포함해야 합니다. + + + + Assert.Throws should contain only a single statement/expression + Assert.Throws는 단일 문/식만 포함해야 합니다. + + \ No newline at end of file diff --git a/src/Analyzers/MSTest.Analyzers/xlf/Resources.pl.xlf b/src/Analyzers/MSTest.Analyzers/xlf/Resources.pl.xlf index b82fdc2980..436d3c6be8 100644 --- a/src/Analyzers/MSTest.Analyzers/xlf/Resources.pl.xlf +++ b/src/Analyzers/MSTest.Analyzers/xlf/Resources.pl.xlf @@ -132,6 +132,16 @@ Typ deklarujący te metody powinien również przestrzegać następujących regu Nie używaj elementu "Assert.AreSame" ani "Assert.AreNotSame" z typami wartości + + Replace '{0}' with format parameters with string.Format or string interpolation + Zamień „{0}” na parametry formatu za pomocą string.Format lub interpolacji ciągu + + + + Avoid using Assert methods with format parameters + Unikaj używania metod Assert z parametrami formatu + + Prefer 'Assert.ThrowsExactly' or 'Assert.ThrowsExactlyAsync' over '[ExpectedException]' as it ensures that only the expected call throws the expected exception. The assert APIs also provide more flexibility and allow you to assert extra properties of the exception. Preferuj opcję „Assert.ThrowsExactly” lub „Assert.ThrowsExactlyAsync” zamiast „[ExpectedException]”, ponieważ gwarantuje ona, że tylko oczekiwane wywołanie rzuci oczekiwany wyjątek. Interfejsy API potwierdzenia zapewniają również większą elastyczność i pozwalają na potwierdzenie dodatkowych właściwości wyjątku. @@ -147,6 +157,16 @@ Typ deklarujący te metody powinien również przestrzegać następujących regu Unikaj elementu „[ExpectedException]” + + Remove the 'DynamicDataSourceType' argument to use the default auto detect behavior + Usuń argument „DynamicDataSourceType”, aby użyć domyślnego zachowania wykrywania automatycznego + + + + Avoid passing an explicit 'DynamicDataSourceType' and use the default auto detect behavior + Unikaj przekazywania jawnego elementu „DynamicDataSourceType” i użyj domyślnego zachowania wykrywania automatycznego + + Do not assert inside 'async void' methods, local functions, or lambdas. Exceptions that are thrown in this context will be unhandled exceptions. When using VSTest under .NET Framework, they will be silently swallowed. When using Microsoft.Testing.Platform or VSTest under modern .NET, they may crash the process. Nie potwierdzaj wewnątrz metod "async void", funkcji lokalnych ani lambda. Wyjątki, które są zgłaszane w tym kontekście, będą nieobsługiwanymi wyjątkami. W przypadku korzystania z narzędzia VSTest w .NET Framework będą one dyskretnie ściszone. W przypadku korzystania z elementu Microsoft.Testing.Platform lub VSTest w nowoczesnych programach .NET mogą one spowodować awarię procesu. @@ -277,8 +297,13 @@ Typ deklarujący te metody powinien również przestrzegać następujących regu - DataRow argument type should match method parameter type. Mismatches occur at indices: {0} - Typ argumentu DataRow powinien być zgodny z typem parametru metody. W indeksach występują niezgodności: {0} + DataRow argument types do not match method parameter types. {0} + Typy argumentów DataRow nie są zgodne z typami parametrów metody. {0} + + + + Parameter '{0}' expects type '{1}', but the provided value has type '{2}' + Parametr „{0}” oczekuje typu „{1}”, ale podana wartość ma typ „{2}” @@ -402,11 +427,21 @@ Typ deklarujący te metody powinien również przestrzegać następujących regu „[DynamicData]” należy ustawić tylko dla metody testowej + + '[DynamicData]' member '{0}.{1}' is a field so you should use 'DynamicDataSourceType.AutoDetect' or 'DynamicDataSourceType.Field' (auto detect is the default when not specified explicitly, and is recommended) + Składowa „[DynamicData]” „{0}.{1}” jest polem, dlatego należy użyć elementu „DynamicDataSourceType.AutoDetect” lub „DynamicDataSourceType.Field” (wykrywanie automatyczne jest ustawieniem domyślnym, gdy nie zostanie jawnie określone i jest zalecane) + + '[DynamicData]' member '{0}.{1}' is a method so you should use 'DynamicDataSourceType.AutoDetect' or 'DynamicDataSourceType.Method' (auto detect is the default when not specified explicitly, and is recommended) '[DynamicData]' składowej "{0}.{1}" jest metodą, dlatego należy użyć elementu "DynamicDataSourceType.AutoDetect" lub "DynamicDataSourceType.Method" (autowykrywanie jest domyślne, gdy nie zostało jawnie określone i jest zalecane) + + '[DynamicData]' member '{0}.{1}' is not a property, method, or field. Only properties, methods, and fields are supported. + Składowa „[DynamicData]” „{0}.{1}” nie jest właściwością, metodą ani polem. Obsługiwane są tylko właściwości, metody i pola. + + '[DynamicData]' member '{0}.{1}' is not a property nor a method. Only properties and methods are supported. składowa '[DynamicData]' "{0}.{1}" nie jest właściwością ani metodą. Obsługiwane są tylko właściwości i metody. @@ -427,6 +462,81 @@ Typ deklarujący te metody powinien również przestrzegać następujących regu Wartość DynamicData powinna być prawidłowa + + When calling async methods that have overloads accepting a CancellationToken parameter, prefer using the overload with TestContext.CancellationToken to enable cooperative cancellation and respect test timeouts. + W przypadku wywoływania metod asynchronicznych, które mają przeciążenia akceptujące parametr CancellationToken, preferuj użycie przeciążenia z argumentem TestContext.CancellationToken, aby umożliwić anulowanie w trybie współpracy i respektowanie limitów czasu testu. + + + + Consider using the overload that accepts a CancellationToken and pass 'TestContext.CancellationToken' + Rozważ użycie przeciążenia, które akceptuje element CancellationToken i przekaż argument „TestContext.CancellationToken” + + + + Flow TestContext.CancellationToken to async operations + Przekaż TestContext.CancellationToken do operacji asynchronicznych + + + + Methods marked with '[GlobalTestInitialize]' or '[GlobalTestCleanup]' should follow the following layout to be valid: +-it can't be declared on a generic class +-it should be 'public' +-it should be 'static' +-it should not be 'async void' +-it should not be a special method (finalizer, operator...). +-it should not be generic +-it should take one parameter of type 'TestContext' +-return type should be 'void', 'Task' or 'ValueTask' + +The type declaring these methods should also respect the following rules: +-The type should be a class +-The class should be 'public' +-The class shouldn't be 'static' +-The class should be marked with '[TestClass]' (or a derived attribute) +-the class should not be generic. + Metody oznaczone znakiem „[GlobalTestInitialize]” lub „[GlobalTestCleanup]” powinny być zgodne z następującym układem, aby były prawidłowe: +— nie może być zadeklarowana w klasie ogólnej +— powinna być typu „public” +— powinna mieć wartość „static” +— nie powinna to być wartość „async void” +— nie powinna to być metoda specjalna (finalizator, operator...). +— nie powinna być ogólna +— powinna przyjmować jeden parametr typu „TestContext” +— zwracany typ powinien mieć wartość „void”, „Taks” lub „ValueTask” + +Typ deklarujący te metody powinien również przestrzegać następujących reguł: +— typ powinien być klasą +— klasa powinna być „publiczna” +— klasa nie powinna mieć wartości „static” +— klasa powinna być oznaczona „[TestClass]” (lub atrybutem pochodnym) +— klasa nie powinna być ogólna. + + + + Global test fixture method '{0}' signature is invalid + Podpis globalnej metody warunków początkowych testu „{0}” jest nieprawidłowy + + + + GlobalTestInitialize and GlobalTestCleanup methods should have valid layout + Metody GlobalTestInitialize i GlobalTestCleanup powinny mieć prawidłowy układ + + + + Methods like Contains, StartsWith, and EndsWith return boolean values that indicate whether the condition was met. Ignoring these return values is likely a mistake. + Metody takie jak Contains, StartsWith i EndsWith zwracają wartości logiczne wskazujące, czy warunek został spełniony. Ignorowanie tych wartości zwrotnych jest prawdopodobnie błędem. + + + + The return value of '{0}' should not be ignored + Zwracana wartość „{0}” nie powinna być ignorowana + + + + Do not ignore the return value of string methods + Nie ignoruj zwracanej wartości metod ciągu + + 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}” @@ -437,6 +547,21 @@ Typ deklarujący te metody powinien również przestrzegać następujących regu Użyj trybu „Assert.Fail” zamiast kończącej się zawsze niepowodzeniem instrukcji asercji + + 'DataTestMethodAttribute' is obsolete and provides no additional functionality over 'TestMethodAttribute'. Use 'TestMethodAttribute' for all test methods, including parameterized tests. + Atrybut „DataTestMethodAttribute” jest przestarzały i nie zapewnia dodatkowych funkcji w stosunku do atrybutu „TestMethodAttribute”. Użyj atrybutu „TestMethodAttribute” dla wszystkich metod testowych, w tym testów sparametryzowanych. + + + + 'DataTestMethod' is obsolete. Use 'TestMethod' instead. + Element „DataTestMethod” jest przestarzały. Zamiast niego użyj elementu „TestMethod”. + + + + Prefer 'TestMethod' over 'DataTestMethod' + Wybieraj element „TestMethod”, anie „DataTestMethod” + + Review or remove the assertion as its condition is known to be always true Przejrzyj lub usuń asercję, ponieważ wiadomo, że jej warunek ma zawsze wartość true @@ -517,6 +642,16 @@ Typ deklarujący te metody powinien również przestrzegać następujących regu Typy publiczne powinny być klasami testowymi + + Use 'Assert.{0}' instead of 'StringAssert.{1}' + Użyj ciągu „Assert.{0}” zamiast ciągu „StringAssert.{1}” + + + + Use 'Assert' instead of 'StringAssert' + Użyj ciągu „Assert” zamiast ciągu „StringAssert” + + Test class '{0}' should be valid Klasa testowa „{0}” powinna być prawidłowa @@ -532,6 +667,21 @@ Typ deklarujący te metody powinien również przestrzegać następujących regu Podpis metody testowej „{0}” jest nieprawidłowy + + TestContext.CancellationToken provides a more direct way to access the cancellation token compared to TestContext.CancellationTokenSource.Token. + Element TestContext.CancellationToken zapewnia bardziej bezpośredni sposób uzyskiwania dostępu do tokenu anulowania w porównaniu z elementem TestContext.CancellationTokenSource.Token. + + + + Use 'TestContext.CancellationToken' instead of 'TestContext.CancellationTokenSource.Token' + Użyj argumentu „TestContext.CancellationToken” zamiast argumentu „TestContext.CancellationTokenSource.Token” + + + + Use TestContext.CancellationToken instead of TestContext.CancellationTokenSource.Token + Użyj argumentu TestContext.CancellationToken zamiast argumentu TestContext.CancellationTokenSource.Token + + Without using 'ClassCleanupBehavior.EndOfClass', the '[ClassCleanup]' will by default be run at the end of the assembly and not at the end of the class. Bez użycia elementu „ClassCleanupBehavior.EndOfClass” element „[ClassCleanup]” będzie domyślnie uruchamiany na końcu zestawu, a nie na końcu klasy. @@ -720,13 +870,13 @@ Typ deklarujący te metody powinien również przestrzegać następujących regu - 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. + Type containing '[TestMethod]' should be marked with '[TestClass]', otherwise the test method will be silently ignored. + Typ zawierający element „[TestMethod]” powinien być oznaczony znakiem „[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 '{0}' contains test methods and should be marked with '[TestClass]' + Typ „{0}” zawiera metody testowe i powinien być oznaczony znakiem „[TestClass]” @@ -744,6 +894,11 @@ Typ deklarujący te metody powinien również przestrzegać następujących regu Asynchroniczne metody testowe nie wymagają sufiksu „Async” + + TestContext parameter is required by MSTest for AssemblyInitialize and ClassInitialize methods + Parametr TestContext jest wymagany przez narzędzie MSTest dla metod AssemblyInitialize i ClassInitialize + + [{0}] can only be set on methods marked with [TestMethod] [{0}] można ustawić tylko dla metod oznaczonych znakiem [TestMethod] @@ -764,6 +919,21 @@ Typ deklarujący te metody powinien również przestrzegać następujących regu Użyj atrybutu "ConditionBaseAttribute" w klasach testu + + Using '[Timeout]' without explicitly setting 'CooperativeCancellation = true' is discouraged. In a future version, cooperative cancellation will become the default behavior. Set 'CooperativeCancellation = true' to opt into the recommended behavior and avoid breaking changes. + Nie rekomenduje się używania elementu „[Timeout]” bez jawnego ustawiania wartości „CooperativeCancellation = true”. W przyszłej wersji anulowanie trybu współpracy będzie zachowaniem domyślnym. Ustaw opcję „CooperativeCancellation = true”, aby włączyć rekomendowane zachowanie i uniknąć zmian powodujących niezgodność. + + + + Use 'CooperativeCancellation = true' with '[Timeout]' to enable cooperative cancellation behavior + Użyj opcji „CooperativeCancellation = true” z limitem czasu „[Timeout]”, aby włączyć zachowanie anulowania trybu współpracy + + + + Use 'CooperativeCancellation = true' with '[Timeout]' + Użyj opcji „CooperativeCancellation = true” w limitem czasu „[Timeout]” + + '[DeploymentItem]' can be specified only on test class or test method Element „[DeploymentItem]” można określić tylko dla klasy testowej lub metody testowej @@ -819,6 +989,36 @@ Typ deklarujący te metody powinien również przestrzegać następujących regu Użyj atrybutu ponawiania dla metody testowej + + TestContext property cannot be accessed in this context + W tym kontekście nie można uzyskać dostępu do właściwości TestContext + + + + TestContext property '{0}' cannot be accessed in '{1}' method + Nie można uzyskać dostępu do właściwości TestContext „{0}” w metodzie „{1}” + + + + Some TestContext properties are only available during test execution and cannot be accessed in assembly initialize, class initialize, class cleanup, or assembly cleanup methods. + Niektóre właściwości TestContext są dostępne tylko podczas wykonywania testu i nie można uzyskać do nich dostępu w metodach inicjowania, inicjowania klasy, oczyszczania klasy ani oczyszczania zestawów. + + + + Assert.Throws methods should contain only a single statement or expression. Multiple statements can be misleading - if the first statement throws, subsequent statements are never executed; if it doesn't throw, it should be moved outside the Assert.Throws. + Metody Assert.Throws powinny zawierać tylko jedną instrukcję lub wyrażenie. Wiele instrukcji może być mylących – jeśli pierwsza instrukcja zgłosi wyjątek, kolejne instrukcje nigdy nie zostaną wykonane; jeśli nie zgłosi wyjątku, powinna zostać przeniesiona poza Assert.Throws. + + + + Assert.Throws should contain only a single statement/expression + Element Assert.Throws powinien zawierać tylko jedną instrukcję/wyrażenie + + + + Assert.Throws should contain only a single statement/expression + Element Assert.Throws powinien zawierać tylko jedną instrukcję/wyrażenie + + \ No newline at end of file diff --git a/src/Analyzers/MSTest.Analyzers/xlf/Resources.pt-BR.xlf b/src/Analyzers/MSTest.Analyzers/xlf/Resources.pt-BR.xlf index db27da787d..232924c5a6 100644 --- a/src/Analyzers/MSTest.Analyzers/xlf/Resources.pt-BR.xlf +++ b/src/Analyzers/MSTest.Analyzers/xlf/Resources.pt-BR.xlf @@ -132,6 +132,16 @@ O tipo que declara esses métodos também deve respeitar as seguintes regras: Não use 'Assert.AreSame' ou 'Assert.AreNotSame' com tipos de valor + + Replace '{0}' with format parameters with string.Format or string interpolation + Substitua '{0}' com parâmetros de formato por cadeia de caracteres. Interpolação de formato ou cadeia de caracteres + + + + Avoid using Assert methods with format parameters + Evite usar métodos Assert com parâmetros de formato + + Prefer 'Assert.ThrowsExactly' or 'Assert.ThrowsExactlyAsync' over '[ExpectedException]' as it ensures that only the expected call throws the expected exception. The assert APIs also provide more flexibility and allow you to assert extra properties of the exception. Prefira 'Assert.ThrowsExactly' ou 'Assert.ThrowsExactlyAsync' em vez de '[ExpectedException]', pois isso garante que apenas a chamada esperada lance a exceção esperada. As APIs de assert também oferecem mais flexibilidade e permitem que você afirme propriedades adicionais da exceção. @@ -147,6 +157,16 @@ O tipo que declara esses métodos também deve respeitar as seguintes regras: Evitar '[ExpectedException]' + + Remove the 'DynamicDataSourceType' argument to use the default auto detect behavior + Remova o argumento "DynamicDataSourceType" para usar o comportamento de detecção automática padrão + + + + Avoid passing an explicit 'DynamicDataSourceType' and use the default auto detect behavior + Evitar passar um "DynamicDataSourceType" explícito e usar o comportamento de detecção automática padrão + + Do not assert inside 'async void' methods, local functions, or lambdas. Exceptions that are thrown in this context will be unhandled exceptions. When using VSTest under .NET Framework, they will be silently swallowed. When using Microsoft.Testing.Platform or VSTest under modern .NET, they may crash the process. Não asserção dentro de métodos 'async void', funções locais ou lambdas. Exceções lançadas neste contexto serão exceções sem tratamento. Ao usar VSTest sob .NET Framework, eles serão silenciosamente ignoradas. Ao usar Microsoft.Testing.Platform ou VSTest em .NET moderno, o processo pode falhar. @@ -277,8 +297,13 @@ O tipo que declara esses métodos também deve respeitar as seguintes regras: - DataRow argument type should match method parameter type. Mismatches occur at indices: {0} - O tipo de argumento DataRow deve corresponder ao tipo de parâmetro do método. Incompatibilidades ocorrem nos índices: {0} + DataRow argument types do not match method parameter types. {0} + Os tipos de argumento do DataRow não correspondem aos tipos de parâmetro do método. {0} + + + + Parameter '{0}' expects type '{1}', but the provided value has type '{2}' + O parâmetro "{0}" espera o tipo "{1}", mas o valor fornecido tem o tipo "{2}" @@ -402,11 +427,21 @@ O tipo que declara esses métodos também deve respeitar as seguintes regras: "[DynamicData]" só deve ser definido em um método de teste + + '[DynamicData]' member '{0}.{1}' is a field so you should use 'DynamicDataSourceType.AutoDetect' or 'DynamicDataSourceType.Field' (auto detect is the default when not specified explicitly, and is recommended) + O membro "[DynamicData]" "{0}.{1}" é um campo — portanto, você deve usar "DynamicDataSourceType.AutoDetect" ou "DynamicDataSourceType.Field" (detectar automaticamente é o padrão quando não especificado explicitamente, e é recomendado) + + '[DynamicData]' member '{0}.{1}' is a method so you should use 'DynamicDataSourceType.AutoDetect' or 'DynamicDataSourceType.Method' (auto detect is the default when not specified explicitly, and is recommended) '[DynamicData]' membro '{0}.{1}' é um método, portanto, você deve usar 'DynamicDataSourceType.AutoDetect' ou 'DynamicDataSourceType.Method' (detectar automaticamente é o padrão quando não especificado explicitamente e é recomendado) + + '[DynamicData]' member '{0}.{1}' is not a property, method, or field. Only properties, methods, and fields are supported. + O membro "[DynamicData]" "{0}.{1}" não é uma propriedade, método ou campo. Somente propriedades, métodos e campos têm suporte. + + '[DynamicData]' member '{0}.{1}' is not a property nor a method. Only properties and methods are supported. '[DynamicData]' membro '{0}.{1}' não é uma propriedade nem um método. Somente propriedades e métodos têm suporte. @@ -427,6 +462,81 @@ O tipo que declara esses métodos também deve respeitar as seguintes regras: DynamicData deve ser válido + + When calling async methods that have overloads accepting a CancellationToken parameter, prefer using the overload with TestContext.CancellationToken to enable cooperative cancellation and respect test timeouts. + Ao chamar métodos assíncronos que têm sobrecargas que aceitam um parâmetro CancellationToken, prefira usar a sobrecarga com TestContext.CancellationToken para habilitar o cancelamento cooperativo e respeitar os limites de tempo do teste. + + + + Consider using the overload that accepts a CancellationToken and pass 'TestContext.CancellationToken' + Considere usar a sobrecarga que aceita um CancellationToken e passar 'TestContext.CancellationToken' + + + + Flow TestContext.CancellationToken to async operations + Fluxo TestContext.CancellationToken para operações assíncronas + + + + Methods marked with '[GlobalTestInitialize]' or '[GlobalTestCleanup]' should follow the following layout to be valid: +-it can't be declared on a generic class +-it should be 'public' +-it should be 'static' +-it should not be 'async void' +-it should not be a special method (finalizer, operator...). +-it should not be generic +-it should take one parameter of type 'TestContext' +-return type should be 'void', 'Task' or 'ValueTask' + +The type declaring these methods should also respect the following rules: +-The type should be a class +-The class should be 'public' +-The class shouldn't be 'static' +-The class should be marked with '[TestClass]' (or a derived attribute) +-the class should not be generic. + Os métodos marcados com "[GlobalTestInitialize]" ou "[GlobalTestCleanup]" devem seguir o seguinte layout para serem válidos: +-não podem ser declarados em uma classe genérica +-devem ser "públicos" +-devem ser "estáticos" +-não devem ser "nulos assíncronos" +-não devem ser um método especial (finalizador, operador...). +-não devem ser genéricos +-devem usar um parâmetro do tipo “TestContext” +-o tipo de retorno deve ser “nulo”, “Tarefa” ou “ValueTask” + +O tipo que declara esses métodos também deve respeitar as seguintes regras: +-O tipo deve ser uma classe +-A classe deve ser "público" +-A classe não deve ser “estático” +-A classe deve ser marcada com “[TestClass]” (ou um atributo derivado) +-a classe não deve ser genérica. + + + + Global test fixture method '{0}' signature is invalid + A assinatura do método de acessório de teste global "{0}" é inválida + + + + GlobalTestInitialize and GlobalTestCleanup methods should have valid layout + Os métodos GlobalTestInitialize e GlobalTestCleanup devem ter um layout válido + + + + Methods like Contains, StartsWith, and EndsWith return boolean values that indicate whether the condition was met. Ignoring these return values is likely a mistake. + Métodos como Contains, StartsWith e EndsWith retornam valores boolianos que indicam se a condição foi atendida. Ignorar esses valores retornados provavelmente é um erro. + + + + The return value of '{0}' should not be ignored + O valor retornado de "{0}" não deve ser ignorado + + + + Do not ignore the return value of string methods + Não ignore o valor retornado dos métodos de cadeia de caracteres + + 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 @@ -437,6 +547,21 @@ O tipo que declara esses métodos também deve respeitar as seguintes regras: Usar "Assert.Fail" em vez de uma asserção sempre com falha + + 'DataTestMethodAttribute' is obsolete and provides no additional functionality over 'TestMethodAttribute'. Use 'TestMethodAttribute' for all test methods, including parameterized tests. + 'DataTestMethodAttribute' está obsoleto e não oferece funcionalidade adicional em relação a 'TestMethodAttribute'. Use 'TestMethodAttribute' para todos os métodos de teste, inclusive testes parametrizados. + + + + 'DataTestMethod' is obsolete. Use 'TestMethod' instead. + 'DataTestMethod' está obsoleto. Use 'TestMethod' em vez disso. + + + + Prefer 'TestMethod' over 'DataTestMethod' + Prefira 'TestMethod' a 'DataTestMethod' + + Review or remove the assertion as its condition is known to be always true Examine ou remova a asserção, pois a sua condição é conhecida por ser sempre verdadeira @@ -517,6 +642,16 @@ O tipo que declara esses métodos também deve respeitar as seguintes regras: Os tipos públicos devem ser classes de teste + + Use 'Assert.{0}' instead of 'StringAssert.{1}' + Use "Assert.{0}" em vez de "StringAssert.{1}" + + + + Use 'Assert' instead of 'StringAssert' + Usar "Assert" em vez de "StringAssert" + + Test class '{0}' should be valid A classe de teste ''{0}'' deve ser válida @@ -532,6 +667,21 @@ O tipo que declara esses métodos também deve respeitar as seguintes regras: A assinatura "{0}" do método de teste é inválida + + TestContext.CancellationToken provides a more direct way to access the cancellation token compared to TestContext.CancellationTokenSource.Token. + TestContext.CancellationToken oferece uma maneira mais direta de acessar o token de cancelamento em comparação com TestContext.CancellationTokenSource.Token. + + + + Use 'TestContext.CancellationToken' instead of 'TestContext.CancellationTokenSource.Token' + Use 'TestContext.CancellationToken' em vez de 'TestContext.CancellationTokenSource.Token' + + + + Use TestContext.CancellationToken instead of TestContext.CancellationTokenSource.Token + Use TestContext.CancellationToken em vez de TestContext.CancellationTokenSource.Token + + Without using 'ClassCleanupBehavior.EndOfClass', the '[ClassCleanup]' will by default be run at the end of the assembly and not at the end of the class. Sem usar o "ClassCleanupBehavior.EndOfClass", o "[ClassCleanup]" será executado, por padrão, no final do assembly e não no final da classe. @@ -720,13 +870,13 @@ O tipo que declara esses métodos também deve respeitar as seguintes regras: - 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. + Type containing '[TestMethod]' should be marked with '[TestClass]', otherwise the test method will be silently ignored. + O tipo que contém '[TestMethod]' deve ser marcado com '[TestClass]', caso contrário, o método de teste será ignorado sem aviso. - 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 '{0}' contains test methods and should be marked with '[TestClass]' + O tipo '{0}' contém métodos de teste e deve ser marcado com '[TestClass]' @@ -744,6 +894,11 @@ O tipo que declara esses métodos também deve respeitar as seguintes regras: Os métodos de teste assíncronos não exigem o sufixo 'Async' + + TestContext parameter is required by MSTest for AssemblyInitialize and ClassInitialize methods + O parâmetro TestContext é obrigatório para métodos AssemblyInitialize e ClassInitialize do MSTest + + [{0}] can only be set on methods marked with [TestMethod] [{0}] só pode ser definido em métodos marcados com [TestMethod] @@ -764,6 +919,21 @@ O tipo que declara esses métodos também deve respeitar as seguintes regras: Usar 'ConditionBaseAttribute' em classes de teste + + Using '[Timeout]' without explicitly setting 'CooperativeCancellation = true' is discouraged. In a future version, cooperative cancellation will become the default behavior. Set 'CooperativeCancellation = true' to opt into the recommended behavior and avoid breaking changes. + Usar '\[Timeout]' sem definir explicitamente 'CooperativeCancellation = true' não é recomendado. Em uma versão futura, o cancelamento cooperativo se tornará o comportamento padrão. Defina 'CooperativeCancellation = true' para optar pelo comportamento recomendado e evitar quebras. + + + + Use 'CooperativeCancellation = true' with '[Timeout]' to enable cooperative cancellation behavior + Use 'CooperativeCancellation = true' com '\[Timeout]' para habilitar o comportamento de cancelamento cooperativo + + + + Use 'CooperativeCancellation = true' with '[Timeout]' + Use 'CooperativeCancellation = true' com '\[Timeout]' + + '[DeploymentItem]' can be specified only on test class or test method '[DeploymentItem]' pode ser especificado apenas na classe de teste ou método de teste @@ -819,6 +989,36 @@ O tipo que declara esses métodos também deve respeitar as seguintes regras: Usar o atributo de repetição no método de teste + + TestContext property cannot be accessed in this context + A propriedade TestContext não pode ser acessada neste contexto + + + + TestContext property '{0}' cannot be accessed in '{1}' method + A propriedade "{0}" de TestContext não pode ser acessada no método "{1}" + + + + Some TestContext properties are only available during test execution and cannot be accessed in assembly initialize, class initialize, class cleanup, or assembly cleanup methods. + Algumas propriedades TestContext só estão disponíveis durante a execução do teste e não podem ser acessadas em métodos de inicialização de assembly, inicialização de classe, limpeza de classe ou limpeza de assembly. + + + + Assert.Throws methods should contain only a single statement or expression. Multiple statements can be misleading - if the first statement throws, subsequent statements are never executed; if it doesn't throw, it should be moved outside the Assert.Throws. + Os métodos Assert.Throws devem conter apenas uma única declaração ou expressão. Várias declarações podem ser enganosas: se a primeira declaração for lançada, as declarações subsequentes nunca serão executadas; se não lançar, ela deverá ser movida para fora do Assert.Throws. + + + + Assert.Throws should contain only a single statement/expression + Assert.Throws deve conter apenas uma única declaração/expressão + + + + Assert.Throws should contain only a single statement/expression + Assert.Throws deve conter apenas uma única declaração/expressão + + \ No newline at end of file diff --git a/src/Analyzers/MSTest.Analyzers/xlf/Resources.ru.xlf b/src/Analyzers/MSTest.Analyzers/xlf/Resources.ru.xlf index f2770a865e..9ec21cbef5 100644 --- a/src/Analyzers/MSTest.Analyzers/xlf/Resources.ru.xlf +++ b/src/Analyzers/MSTest.Analyzers/xlf/Resources.ru.xlf @@ -135,6 +135,16 @@ The type declaring these methods should also respect the following rules: Не используйте Assert.AreSame или Assert.AreNotSame с типами значений + + Replace '{0}' with format parameters with string.Format or string interpolation + Замените "{0}" на параметры формата с помощью string.Format или интерполяции строк + + + + Avoid using Assert methods with format parameters + Избегайте использования методов Assert с параметрами форматирования + + Prefer 'Assert.ThrowsExactly' or 'Assert.ThrowsExactlyAsync' over '[ExpectedException]' as it ensures that only the expected call throws the expected exception. The assert APIs also provide more flexibility and allow you to assert extra properties of the exception. Предпочтите "Assert.ThrowsExactly' или 'Assert.ThrowsExactlyAsync" вместо "[ExpectedException]", так как это гарантирует, что только ожидаемый вызов вызовет ожидаемое исключение. API-интерфейсы утверждения также обеспечивают дополнительную гибкость и позволяют утверждать дополнительные свойства исключения. @@ -150,6 +160,16 @@ The type declaring these methods should also respect the following rules: Избегать "[ExpectedException]" + + Remove the 'DynamicDataSourceType' argument to use the default auto detect behavior + Удалите аргумент DynamicDataSourceType, чтобы использовать поведение автоматического обнаружения по умолчанию + + + + Avoid passing an explicit 'DynamicDataSourceType' and use the default auto detect behavior + Избегайте передачи явного DynamicDataSourceType и используйте поведение автоматического обнаружения по умолчанию + + Do not assert inside 'async void' methods, local functions, or lambdas. Exceptions that are thrown in this context will be unhandled exceptions. When using VSTest under .NET Framework, they will be silently swallowed. When using Microsoft.Testing.Platform or VSTest under modern .NET, they may crash the process. Не подтверяйте внутри методов async void, локальных функций или лямбда-выражений. Исключения, вызванные в этом контексте, будут необработанные исключения. При использовании VSTest платформа .NET Framework, они будут пропущены без уведомления. При использовании Microsoft.Testing.Platform или VSTest в современной версии .NET они могут привести к сбою процесса. @@ -283,8 +303,13 @@ The type declaring these methods should also respect the following rules: - DataRow argument type should match method parameter type. Mismatches occur at indices: {0} - Тип аргумента DataRow должен соответствовать типу параметра метода. Обнаружены несовпадения в следующих индексах: {0} + DataRow argument types do not match method parameter types. {0} + Типы аргументов DataRow не соответствуют типам параметров метода. {0} + + + + Parameter '{0}' expects type '{1}', but the provided value has type '{2}' + Параметр "{0}" ожидает тип "{1}", но предоставленное значение относится к типу "{2}" @@ -408,11 +433,21 @@ The type declaring these methods should also respect the following rules: "[DynamicData]" следует задавать только для метода теста + + '[DynamicData]' member '{0}.{1}' is a field so you should use 'DynamicDataSourceType.AutoDetect' or 'DynamicDataSourceType.Field' (auto detect is the default when not specified explicitly, and is recommended) + Элемент "[DynamicData]" "{0}.{1}" является полем, поэтому следует использовать "DynamicDataSourceType.AutoDetect" или "DynamicDataSourceType.Field" (автообнаружение используется по умолчанию, если не указано явно, и является рекомендуемым) + + '[DynamicData]' member '{0}.{1}' is a method so you should use 'DynamicDataSourceType.AutoDetect' or 'DynamicDataSourceType.Method' (auto detect is the default when not specified explicitly, and is recommended) '[DynamicData]' "{0}.{1}" является методом, поэтому следует использовать "DynamicDataSourceType.AutoDetect" или "DynamicDataSourceType.Method" (автообнаружение используется по умолчанию, если не указано явно, и рекомендуется) + + '[DynamicData]' member '{0}.{1}' is not a property, method, or field. Only properties, methods, and fields are supported. + Элемент "[DynamicData]" "{0}.{1}" не является свойством, методом или полем. Поддерживаются только свойства, методы и поля. + + '[DynamicData]' member '{0}.{1}' is not a property nor a method. Only properties and methods are supported. '[DynamicData]' "{0}.{1}" не является свойством или методом. Поддерживаются только свойства и методы. @@ -433,6 +468,81 @@ The type declaring these methods should also respect the following rules: Значение DynamicData должно быть допустимым + + When calling async methods that have overloads accepting a CancellationToken parameter, prefer using the overload with TestContext.CancellationToken to enable cooperative cancellation and respect test timeouts. + При вызове асинхронных методов с перегрузками, принимающими параметр CancellationToken, рекомендуется использовать перегрузку с TestContext.CancellationTokenSource.Token, чтобы обеспечить совместные действия по отмене и учитывать лимиты времени ожидания тестов. + + + + Consider using the overload that accepts a CancellationToken and pass 'TestContext.CancellationToken' + Рассмотрите возможность использования перегрузки, которая принимает CancellationToken, и передавайте "TestContext.CancellationToken" + + + + Flow TestContext.CancellationToken to async operations + Передача TestContext.CancellationToken в асинхронные операции + + + + Methods marked with '[GlobalTestInitialize]' or '[GlobalTestCleanup]' should follow the following layout to be valid: +-it can't be declared on a generic class +-it should be 'public' +-it should be 'static' +-it should not be 'async void' +-it should not be a special method (finalizer, operator...). +-it should not be generic +-it should take one parameter of type 'TestContext' +-return type should be 'void', 'Task' or 'ValueTask' + +The type declaring these methods should also respect the following rules: +-The type should be a class +-The class should be 'public' +-The class shouldn't be 'static' +-The class should be marked with '[TestClass]' (or a derived attribute) +-the class should not be generic. + Чтобы метод, помеченный "[GlobalTestInitialize]" или "[GlobalTestCleanup]", был допустимым, он должен соответствовать следующей структуре: +– не может быть объявлен для универсального класса ("generic") +– должен быть общедоступным ("public") +– должен быть статическим ("static") +– не должен быть асинхронным и не возвращающим значения ("async void") +– не должен быть специальным (метод завершения, оператор…). +– не должен быть общим ("generic") +– должен принимать один параметр типа "TestContext" +– должен иметь тип возвращаемого значения "void", "Task" или "ValueTask" + +Тип, объявляющий такие методы, также должен соответствовать следующим правилам: +– должен быть классом +– класс должен быть общедоступным ("public") +– не должен быть статическим ("static") +– должен быть помечен как "[TestClass]" (или производный атрибут) +– не должен быть универсальным ("generic"). + + + + Global test fixture method '{0}' signature is invalid + Подпись метода глобального средства тестирования "{0}" недопустима + + + + GlobalTestInitialize and GlobalTestCleanup methods should have valid layout + Методы GlobalTestInitialize и GlobalTestCleanup должны использовать допустимый макет + + + + Methods like Contains, StartsWith, and EndsWith return boolean values that indicate whether the condition was met. Ignoring these return values is likely a mistake. + Такие методы, как Contains, StartsWith и EndsWith, возвращают логические значения, указывающие, выполнено ли условие. Игнорирование этих возвращаемых значений, скорее всего, является ошибкой. + + + + The return value of '{0}' should not be ignored + Возвращаемое значение "{0}" не должно игнорироваться + + + + Do not ignore the return value of string methods + Не игнорируйте возвращаемое значение строковых методов + + Use 'Assert.Fail' instead of an always-failing 'Assert.{0}' assert Используйте "Assert.Fail" вместо утверждения с постоянным сбоем "Assert.{0}" @@ -443,6 +553,21 @@ The type declaring these methods should also respect the following rules: Используйте "Assert.Fail" вместо утверждения с постоянным сбоем + + 'DataTestMethodAttribute' is obsolete and provides no additional functionality over 'TestMethodAttribute'. Use 'TestMethodAttribute' for all test methods, including parameterized tests. + Атрибут "DataTestMethodAttribute" устарел и не предоставляет дополнительных функций по сравнению с "TestMethodAttribute". Используйте '"TestMethodAttribute" для всех методов тестирования, включая параметризованные тесты. + + + + 'DataTestMethod' is obsolete. Use 'TestMethod' instead. + "DataTestMethod" устарел. Используйте вместо него "TestMethod". + + + + Prefer 'TestMethod' over 'DataTestMethod' + Следует предпочитать метод "TestMethod" методу "DataTestMethod" + + Review or remove the assertion as its condition is known to be always true Проверьте или удалите утверждение, так как известно, что его условие всегда истинно. @@ -523,6 +648,16 @@ The type declaring these methods should also respect the following rules: Общедоступные типы должны быть тестовыми классами + + Use 'Assert.{0}' instead of 'StringAssert.{1}' + Используйте "Assert.{0}" вместо "StringAssert.{1}" + + + + Use 'Assert' instead of 'StringAssert' + Используйте "Assert" вместо "StringAssert" + + Test class '{0}' should be valid Тестовый класс "{0}" должен быть допустимым @@ -538,6 +673,21 @@ The type declaring these methods should also respect the following rules: Подпись тестового метода "{0}" недействительна + + TestContext.CancellationToken provides a more direct way to access the cancellation token compared to TestContext.CancellationTokenSource.Token. + TestContext.CancellationToken предоставляет более прямой доступ к маркеру отмены по сравнению с TestContext.CancellationTokenSource.Token. + + + + Use 'TestContext.CancellationToken' instead of 'TestContext.CancellationTokenSource.Token' + Используйте "TestContext.CancellationToken" вместо "TestContext.CancellationTokenSource.Token" + + + + Use TestContext.CancellationToken instead of TestContext.CancellationTokenSource.Token + Используйте TestContext.CancellationToken вместо TestContext.CancellationTokenSource.Token + + Without using 'ClassCleanupBehavior.EndOfClass', the '[ClassCleanup]' will by default be run at the end of the assembly and not at the end of the class. Без использования "ClassCleanupBehavior.EndOfClass" "[ClassCleanup]" по умолчанию будет выполняться в конце сборки, а не в конце класса. @@ -732,13 +882,13 @@ The type declaring these methods should also respect the following rules: - Type contaning '[TestMethod]' should be marked with '[TestClass]', otherwise the test method will be silently ignored. + Type containing '[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 '{0}' contains test methods and should be marked with '[TestClass]' + Тип "{0}" содержит тестовые методы и должен быть помечен как "[TestClass]" @@ -756,6 +906,11 @@ The type declaring these methods should also respect the following rules: Асинхронные методы теста не требуют суффикса Async + + TestContext parameter is required by MSTest for AssemblyInitialize and ClassInitialize methods + Параметр TestContext необходим для MSTest в методах AssemblyInitialize и ClassInitialize + + [{0}] can only be set on methods marked with [TestMethod] [{0}] можно задать только для методов с пометкой [TestMethod] @@ -776,6 +931,21 @@ The type declaring these methods should also respect the following rules: Использовать ConditionBaseAttribute для тестовых классов + + Using '[Timeout]' without explicitly setting 'CooperativeCancellation = true' is discouraged. In a future version, cooperative cancellation will become the default behavior. Set 'CooperativeCancellation = true' to opt into the recommended behavior and avoid breaking changes. + Использование "[Timeout]" без явной настройки "CooperativeCancellation = true" не рекомендуется. В будущей версии кооперативная отмена станет поведением по умолчанию. Настройте "CooperativeCancellation = true", чтобы согласиться с рекомендуемым поведением и избежать критических изменений. + + + + Use 'CooperativeCancellation = true' with '[Timeout]' to enable cooperative cancellation behavior + Используйте "CooperativeCancellation = true" с "[Timeout]", чтобы включить поведение кооперативной отмены + + + + Use 'CooperativeCancellation = true' with '[Timeout]' + Использовать "CooperativeCancellation = true" с "[Timeout]" + + '[DeploymentItem]' can be specified only on test class or test method Указать "[DeploymentItem]" можно только для тестового класса или метода. @@ -831,6 +1001,36 @@ The type declaring these methods should also respect the following rules: Использовать атрибут retry в тестовом методе + + TestContext property cannot be accessed in this context + Свойство TestContext недоступно в этом контексте + + + + TestContext property '{0}' cannot be accessed in '{1}' method + Свойство TestContext "{0}" недоступно в методе "{1}" + + + + Some TestContext properties are only available during test execution and cannot be accessed in assembly initialize, class initialize, class cleanup, or assembly cleanup methods. + Некоторые свойства TestContext доступны только во время выполнения теста и недоступны в методах инициализации сборки, инициализации класса, очистки класса или очистки сборки. + + + + Assert.Throws methods should contain only a single statement or expression. Multiple statements can be misleading - if the first statement throws, subsequent statements are never executed; if it doesn't throw, it should be moved outside the Assert.Throws. + Методы Assert.Throws должны содержать только одну инструкцию или выражение. Несколько инструкций могут вводить в заблуждение: если первое выражение вызывает исключение, последующие инструкции не выполняются; если исключение не возникает, его следует вынести за пределы Assert.Throws. + + + + Assert.Throws should contain only a single statement/expression + Assert.Throws должен содержать только одну инструкцию/выражение + + + + Assert.Throws should contain only a single statement/expression + Assert.Throws должен содержать только одну инструкцию/выражение + + \ No newline at end of file diff --git a/src/Analyzers/MSTest.Analyzers/xlf/Resources.tr.xlf b/src/Analyzers/MSTest.Analyzers/xlf/Resources.tr.xlf index 5e0f02d87f..e30a584b2a 100644 --- a/src/Analyzers/MSTest.Analyzers/xlf/Resources.tr.xlf +++ b/src/Analyzers/MSTest.Analyzers/xlf/Resources.tr.xlf @@ -132,6 +132,16 @@ Bu yöntemleri bildiren tipin ayrıca aşağıdaki kurallara uyması gerekir: Değer türleriyle 'Assert.AreSame' veya 'Assert.AreNotSame' kullanma + + Replace '{0}' with format parameters with string.Format or string interpolation + '{0}' ifadesini string.Format veya string interpolation format parametreleriyle değiştirin + + + + Avoid using Assert methods with format parameters + Biçim parametreleri ile Assert yöntemlerini kullanmaktan kaçının + + Prefer 'Assert.ThrowsExactly' or 'Assert.ThrowsExactlyAsync' over '[ExpectedException]' as it ensures that only the expected call throws the expected exception. The assert APIs also provide more flexibility and allow you to assert extra properties of the exception. Yalnızca beklenen çağrının beklenen özel durumu oluşturmasını sağladığı için '[ExpectedException]' yerine 'Assert.ThrowsExactly' veya 'Assert.ThrowsExceptionAsync' tercih edin. Onaylama API'leri de daha fazla esneklik sağlar ve özel durumun ek özelliklerini onaylamanıza izin verir. @@ -147,6 +157,16 @@ Bu yöntemleri bildiren tipin ayrıca aşağıdaki kurallara uyması gerekir: '[ExpectedException]' kullanmayın + + Remove the 'DynamicDataSourceType' argument to use the default auto detect behavior + Varsayılan otomatik algılama davranışını kullanmak için ‘DynamicDataSourceType’ bağımsız değişkenini kaldırın + + + + Avoid passing an explicit 'DynamicDataSourceType' and use the default auto detect behavior + Açık bir ‘DynamicDataSourceType’ geçirmeyin ve varsayılan otomatik algılama davranışını kullanın + + Do not assert inside 'async void' methods, local functions, or lambdas. Exceptions that are thrown in this context will be unhandled exceptions. When using VSTest under .NET Framework, they will be silently swallowed. When using Microsoft.Testing.Platform or VSTest under modern .NET, they may crash the process. 'async void' metotları, yerel işlevler veya lambdalar içinde onaylamayın. Bu bağlamda oluşturulan özel durumlar işlenmeyen özel durumlar olacak. VsTest'i .NET Framework, sessizce kırılır. Modern .NET altında Microsoft.Testing.Platform veya VSTest kullanırken işlemi kilitler. @@ -277,8 +297,13 @@ Bu yöntemleri bildiren tipin ayrıca aşağıdaki kurallara uyması gerekir: - DataRow argument type should match method parameter type. Mismatches occur at indices: {0} - DataRow bağımsız değişken türü, yöntem parametresinin türüyle eşleşmelidir. Dizinler sırasında uyuşmazlıklar oluştu: {0} + DataRow argument types do not match method parameter types. {0} + DataRow bağımsız değişken türleri, yöntem parametre türleriyle eşleşmiyor. {0} + + + + Parameter '{0}' expects type '{1}', but the provided value has type '{2}' + Parametre ‘{0}’ tipi ‘{1}’ bekliyor, ancak verilen değer ‘{2}’ tipindedir @@ -402,11 +427,21 @@ Bu yöntemleri bildiren tipin ayrıca aşağıdaki kurallara uyması gerekir: '[DynamicData]' yalnızca bir test yönteminde ayarlanmalıdır + + '[DynamicData]' member '{0}.{1}' is a field so you should use 'DynamicDataSourceType.AutoDetect' or 'DynamicDataSourceType.Field' (auto detect is the default when not specified explicitly, and is recommended) + ‘[DynamicData]’ üyesi ‘{0}.{1}’, bir alandır, bu nedenle ‘DynamicDataSourceType.AutoDetect’ veya ‘DynamicDataSourceType.Field’ kullanmalısınız (otomatik algılama, açıkça belirtilmediğinde varsayılan ayardır ve önerilir) + + '[DynamicData]' member '{0}.{1}' is a method so you should use 'DynamicDataSourceType.AutoDetect' or 'DynamicDataSourceType.Method' (auto detect is the default when not specified explicitly, and is recommended) '[DynamicData]' üyesi '{0}.{1}' bir metot olduğundan 'DynamicDataSourceType.AutoDetect' veya 'DynamicDataSourceType.Method' kullanmalısınız (otomatik algılama açıkça belirtilmediğinden varsayılandır ve önerilir) + + '[DynamicData]' member '{0}.{1}' is not a property, method, or field. Only properties, methods, and fields are supported. + ‘[DynamicData]’ üyesi ‘{0}.{1}’, bir özellik, yöntem veya alan değildir. Yalnızca özellikler, yöntemler ve alanlar desteklenir. + + '[DynamicData]' member '{0}.{1}' is not a property nor a method. Only properties and methods are supported. '[DynamicData]' '{0}.{1}' üyesi bir özellik veya yöntem değil. Yalnızca özellikler ve yöntemler desteklenir. @@ -427,6 +462,81 @@ Bu yöntemleri bildiren tipin ayrıca aşağıdaki kurallara uyması gerekir: DynamicData geçerli olmalıdır + + When calling async methods that have overloads accepting a CancellationToken parameter, prefer using the overload with TestContext.CancellationToken to enable cooperative cancellation and respect test timeouts. + CancellationToken parametresini kabul eden aşırı yüklemeleri olan asenkron yöntemleri çağırırken, işbirliğine dayalı iptal işlemini etkinleştirmek ve test zaman aşımlarını dikkate almak için TestContext.CancellationToken ile aşırı yüklemeyi kullanmayı tercih edin. + + + + Consider using the overload that accepts a CancellationToken and pass 'TestContext.CancellationToken' + CancellationToken'ı kabul eden aşırı yüklemeyi kullanmayı düşünün ve 'TestContext.CancellationToken'i geçirin + + + + Flow TestContext.CancellationToken to async operations + TestContext.CancellationToken'ı asenkron işlemler için akışa aktarın + + + + Methods marked with '[GlobalTestInitialize]' or '[GlobalTestCleanup]' should follow the following layout to be valid: +-it can't be declared on a generic class +-it should be 'public' +-it should be 'static' +-it should not be 'async void' +-it should not be a special method (finalizer, operator...). +-it should not be generic +-it should take one parameter of type 'TestContext' +-return type should be 'void', 'Task' or 'ValueTask' + +The type declaring these methods should also respect the following rules: +-The type should be a class +-The class should be 'public' +-The class shouldn't be 'static' +-The class should be marked with '[TestClass]' (or a derived attribute) +-the class should not be generic. + ‘[GlobalTestInitialize]’ veya ‘[GlobalTestCleanup]’ ile işaretlenmiş yöntemlerin geçerli olabilmesi için şu düzeni izlemesi gerekir: +-genel bir sınıfta tanımlanamaz +-'public' olmalıdır +-'static' olmalıdır +-'async void' olmamalıdır +-özel bir metot (sonlandırıcı, işleç...) olmamalıdır. +- genel olmamalıdır +- 'TestContext' türünden bir parametre almalıdır +- dönüş türü 'void', 'Task' veya 'ValueTask' olmalıdır + +Bu metotları bildiren türün ayrıca aşağıdaki kurallara uyması gerekir: +-Tür bir sınıf olmalıdır +-Sınıf ‘public’ olmalıdır +-Sınıf 'static' olmamalıdır +-Sınıf '[TestClass]' (veya türetilmiş bir öznitelik) ile işaretlenmelidir +-sınıf genel olmamalıdır. + + + + Global test fixture method '{0}' signature is invalid + Global test düzeni yöntemi ‘{0}’ imzası geçersiz + + + + GlobalTestInitialize and GlobalTestCleanup methods should have valid layout + GlobalTestInitialize ve GlobalTestCleanup yöntemleri geçerli bir düzene sahip olmalıdır + + + + Methods like Contains, StartsWith, and EndsWith return boolean values that indicate whether the condition was met. Ignoring these return values is likely a mistake. + Contains, StartsWith ve EndsWith gibi yöntemler, koşulun karşılanıp karşılanmadığını belirten boole değerleri döndürür. Bu dönüş değerlerini yok saymak büyük olasılıkla bir hataya yol açar. + + + + The return value of '{0}' should not be ignored + '{0}' değerinin dönüş değeri yok sayılmamalıdır + + + + Do not ignore the return value of string methods + Dize yöntemlerinin dönüş değerini yok saymayın + + 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 @@ -437,6 +547,21 @@ Bu yöntemleri bildiren tipin ayrıca aşağıdaki kurallara uyması gerekir: Her zaman başarısız olan onaylama yerine 'Assert.Fail' seçeneğini kullanın + + 'DataTestMethodAttribute' is obsolete and provides no additional functionality over 'TestMethodAttribute'. Use 'TestMethodAttribute' for all test methods, including parameterized tests. + 'DataTestMethodAttribute' artık kullanılmıyor ve 'TestMethodAttribute' üzerinde ek bir işlevsellik sunmuyor. Parametreli testler de dahil, tüm test yöntemleri için 'TestMethodAttribute' kullanın. + + + + 'DataTestMethod' is obsolete. Use 'TestMethod' instead. + 'DataTestMethod' artık kullanılmıyor. Bunun yerine 'TestMethod' kullanın. + + + + Prefer 'TestMethod' over 'DataTestMethod' + 'DataTestMethod' yerine 'TestMethod' yöntemini tercih edin + + Review or remove the assertion as its condition is known to be always true Koşulu her zaman true olarak bilinen onaylamayı gözden geçirin veya kaldırın @@ -517,6 +642,16 @@ Bu yöntemleri bildiren tipin ayrıca aşağıdaki kurallara uyması gerekir: Genel türler test sınıfları olmalıdır + + Use 'Assert.{0}' instead of 'StringAssert.{1}' + ‘StringAssert.{1}’ yerine ‘Assert.{0}’ kullanın + + + + Use 'Assert' instead of 'StringAssert' + ‘StringAssert’ yerine ‘Assert’ kullanın + + Test class '{0}' should be valid '{0}' test sınıfı geçerli olmalıdır @@ -532,6 +667,21 @@ Bu yöntemleri bildiren tipin ayrıca aşağıdaki kurallara uyması gerekir: '{0}' test yöntemi imzası geçersiz + + TestContext.CancellationToken provides a more direct way to access the cancellation token compared to TestContext.CancellationTokenSource.Token. + TestContext.CancellationToken, TestContext.CancellationTokenSource.Token'a kıyasla iptal belirtecine erişmek için daha doğrudan bir yol sağlar. + + + + Use 'TestContext.CancellationToken' instead of 'TestContext.CancellationTokenSource.Token' + 'TestContext.CancellationTokenSource.Token' yerine ‘TestContext.CancellationToken’ kullanın + + + + Use TestContext.CancellationToken instead of TestContext.CancellationTokenSource.Token + TestContext.CancellationTokenSource.Token yerine TestContext.CancellationToken kullanın + + Without using 'ClassCleanupBehavior.EndOfClass', the '[ClassCleanup]' will by default be run at the end of the assembly and not at the end of the class. 'ClassCleanupBehavior.EndOfClass' kullanılmadığında, '[ClassCleanup]' varsayılan olarak sınıfın sonunda değil derlemenin sonunda çalıştırılır. @@ -722,13 +872,13 @@ Bu yöntemleri bildiren tipin ayrıca aşağıdaki kurallara uyması gerekir: - Type contaning '[TestMethod]' should be marked with '[TestClass]', otherwise the test method will be silently ignored. + Type containing '[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 '{0}' contains test methods and should be marked with '[TestClass]' + '{0}' türü test yöntemleri içeriyor ve '[TestClass]' ile işaretlenmeli @@ -746,6 +896,11 @@ Bu yöntemleri bildiren tipin ayrıca aşağıdaki kurallara uyması gerekir: Asenkron test metotları için 'Async' soneki gerekmez + + TestContext parameter is required by MSTest for AssemblyInitialize and ClassInitialize methods + AssemblyInitialize ve ClassInitialize yöntemleri için MSTest tarafından TestContext parametresi gereklidir. + + [{0}] can only be set on methods marked with [TestMethod] [{0}] yalnızca [TestMethod] ile işaretli yöntemlerde ayarlanabilir @@ -766,6 +921,21 @@ Bu yöntemleri bildiren tipin ayrıca aşağıdaki kurallara uyması gerekir: Test sınıflarda 'ConditionBaseAttribute' kullanın + + Using '[Timeout]' without explicitly setting 'CooperativeCancellation = true' is discouraged. In a future version, cooperative cancellation will become the default behavior. Set 'CooperativeCancellation = true' to opt into the recommended behavior and avoid breaking changes. + '[Timeout]' ayarını 'CooperativeCancellation = true' olarak açıkça ayarlamadan kullanmak önerilmez. Gelecek bir sürümde, birlikte iptal etme varsayılan davranış haline gelecektir. Önerilen davranışı kabul etmek ve uyumsuzlukları önlemek için 'CooperativeCancellation = true' değerini ayarlayın. + + + + Use 'CooperativeCancellation = true' with '[Timeout]' to enable cooperative cancellation behavior + '[Timeout]' ile 'CooperativeCancellation = true' kullanarak birlikte iptal etme davranışını etkinleştirin + + + + Use 'CooperativeCancellation = true' with '[Timeout]' + '[Timeout]' ile 'CooperativeCancellation = true' kullanın + + '[DeploymentItem]' can be specified only on test class or test method '[DeploymentItem]' yalnızca test sınıfında veya test yönteminde belirtilebilir @@ -821,6 +991,36 @@ Bu yöntemleri bildiren tipin ayrıca aşağıdaki kurallara uyması gerekir: Test yönteminde yeniden deneme özniteliğini kullan + + TestContext property cannot be accessed in this context + TestContext özelliğine bu bağlamda erişilemiyor + + + + TestContext property '{0}' cannot be accessed in '{1}' method + TestContext '{0}' özelliğine '{1}' yönteminde erişilemiyor + + + + Some TestContext properties are only available during test execution and cannot be accessed in assembly initialize, class initialize, class cleanup, or assembly cleanup methods. + Bazı TestContext özellikleri yalnızca test yürütmesi sırasında kullanılabilir ve derleme başlatma, sınıf başlatma, sınıf temizleme veya derleme temizleme yöntemlerinde erişilemez. + + + + Assert.Throws methods should contain only a single statement or expression. Multiple statements can be misleading - if the first statement throws, subsequent statements are never executed; if it doesn't throw, it should be moved outside the Assert.Throws. + Assert.Throws yöntemleri yalnızca tek bir deyim veya ifade içermelidir. Birden fazla deyim yanıltıcı olabilir; eğer ilk deyim bir sonuç oluşturursa, sonraki deyimler asla çalıştırılmaz; eğer sonuç oluşturmazsa, bu deyim Assert.Throws dışına alınmalıdır. + + + + Assert.Throws should contain only a single statement/expression + Assert.Throws yalnızca tek bir deyim/ifade içermelidir. + + + + Assert.Throws should contain only a single statement/expression + Assert.Throws yalnızca tek bir deyim/ifade içermelidir. + + \ No newline at end of file diff --git a/src/Analyzers/MSTest.Analyzers/xlf/Resources.zh-Hans.xlf b/src/Analyzers/MSTest.Analyzers/xlf/Resources.zh-Hans.xlf index ff54e2c6e7..329c91d36f 100644 --- a/src/Analyzers/MSTest.Analyzers/xlf/Resources.zh-Hans.xlf +++ b/src/Analyzers/MSTest.Analyzers/xlf/Resources.zh-Hans.xlf @@ -132,6 +132,16 @@ The type declaring these methods should also respect the following rules: 不要将 “Assert.AreSame” 或 “Assert.AreNotSame” 与值类型一起使用 + + Replace '{0}' with format parameters with string.Format or string interpolation + 将 ‘{0}’ 替换为 string.Format 或字符串内插 + + + + Avoid using Assert methods with format parameters + 避免将 Assert 方法与格式参数配合使用 + + Prefer 'Assert.ThrowsExactly' or 'Assert.ThrowsExactlyAsync' over '[ExpectedException]' as it ensures that only the expected call throws the expected exception. The assert APIs also provide more flexibility and allow you to assert extra properties of the exception. 首选“Assert.ThrowsExactly”或“Assert.ThrowsExactlyAsync”,而不是“[ExpectedException]”,因为它确保只有预期调用才会引发预期异常。断言 API 还提供更多的灵活性,并允许你断言异常的额外属性。 @@ -147,6 +157,16 @@ The type declaring these methods should also respect the following rules: 避免 "[ExpectedException]" + + Remove the 'DynamicDataSourceType' argument to use the default auto detect behavior + 移除 "DynamicDataSourceType" 参数以使用默认的自动检测行为 + + + + Avoid passing an explicit 'DynamicDataSourceType' and use the default auto detect behavior + 避免传递显式 "DynamicDataSourceType" 并使用默认的自动检测行为 + + Do not assert inside 'async void' methods, local functions, or lambdas. Exceptions that are thrown in this context will be unhandled exceptions. When using VSTest under .NET Framework, they will be silently swallowed. When using Microsoft.Testing.Platform or VSTest under modern .NET, they may crash the process. 不要在 “async void” 方法、本地函数或 lambdas 内断言。在此上下文中引发的异常将是未经处理的异常。在.NET Framework下使用 VSTest 时,将无提示地接受这些测试。在现代 .NET 下使用 Microsoft.Testing.Platform 或 VSTest 时,它们可能会导致进程崩溃。 @@ -277,8 +297,13 @@ The type declaring these methods should also respect the following rules: - DataRow argument type should match method parameter type. Mismatches occur at indices: {0} - DataRow 参数类型应与方法参数类型匹配。索引处出现不匹配: {0} + DataRow argument types do not match method parameter types. {0} + DataRow 参数类型与方法参数类型不匹配。{0} + + + + Parameter '{0}' expects type '{1}', but the provided value has type '{2}' + 参数 ‘{0}’ 需要类型 ‘{1}’,但提供的值类型为 ‘{2}’ @@ -402,11 +427,21 @@ The type declaring these methods should also respect the following rules: 应仅对测试方法设置 "[DynamicData]" + + '[DynamicData]' member '{0}.{1}' is a field so you should use 'DynamicDataSourceType.AutoDetect' or 'DynamicDataSourceType.Field' (auto detect is the default when not specified explicitly, and is recommended) + "[DynamicData]" 成员“{0}.{1}”是字段,因此应使用 "DynamicDataSourceType.AutoDetect" 或 "DynamicDataSourceType.Field" (未显式指定时,默认为自动检测,建议使用此设置) + + '[DynamicData]' member '{0}.{1}' is a method so you should use 'DynamicDataSourceType.AutoDetect' or 'DynamicDataSourceType.Method' (auto detect is the default when not specified explicitly, and is recommended) '[DynamicData]' 成员 “{0}.{1}” 是一个方法,因此应使用 “DynamicDataSourceType.AutoDetect” 或 “DynamicDataSourceType.Method”,(未显式指定时,自动检测为默认值,建议) + + '[DynamicData]' member '{0}.{1}' is not a property, method, or field. Only properties, methods, and fields are supported. + "[DynamicData]" 成员“{0}.{1}”不是属性、方法或字段。仅支持属性、方法和字段。 + + '[DynamicData]' member '{0}.{1}' is not a property nor a method. Only properties and methods are supported. '[DynamicData]' 成员 “{0}.{1}” 既不是属性也不是方法。仅支持属性和方法。 @@ -427,6 +462,81 @@ The type declaring these methods should also respect the following rules: DynamicData 应有效 + + When calling async methods that have overloads accepting a CancellationToken parameter, prefer using the overload with TestContext.CancellationToken to enable cooperative cancellation and respect test timeouts. + 在调用包含接受 CancellationToken 参数的重载的异步方法时,请优先使用带 TestContext.CancellationToken 的重载以启用协作取消并遵守测试超时。 + + + + Consider using the overload that accepts a CancellationToken and pass 'TestContext.CancellationToken' + 考虑使用接受 CancellationToken 的重载,并传递 'TestContext.CancellationToken' + + + + Flow TestContext.CancellationToken to async operations + 将 TestContext.CancellationToken 传递给异步操作 + + + + Methods marked with '[GlobalTestInitialize]' or '[GlobalTestCleanup]' should follow the following layout to be valid: +-it can't be declared on a generic class +-it should be 'public' +-it should be 'static' +-it should not be 'async void' +-it should not be a special method (finalizer, operator...). +-it should not be generic +-it should take one parameter of type 'TestContext' +-return type should be 'void', 'Task' or 'ValueTask' + +The type declaring these methods should also respect the following rules: +-The type should be a class +-The class should be 'public' +-The class shouldn't be 'static' +-The class should be marked with '[TestClass]' (or a derived attribute) +-the class should not be generic. + 标记有 "[GlobalTestInitialize]" 或 "[GlobalTestCleanup]" 的方法应遵循以下布局才会有效: +- 不能在泛型类上声明它 +- 它应为 "public" +- 它应为 "static" +- 它不应为 "async void" +- 它不应是特殊方法(终结器、运算符...)。 +- 它不应是泛型的 +- 它应采用 "TestContext" 类型的一个参数 +- 返回类型应为 "void"、"Task" 或 "ValueTask" + +声明这些方法的类型还应遵循以下规则: +- 类型应为类 +- 类应该为 "public" +- 类不应为 "static" +- 应使用 "[TestClass]"(或派生属性)标记类 +- 类不应是泛型的。 + + + + Global test fixture method '{0}' signature is invalid + 全局测试固定例程方法 "{0}" 签名无效 + + + + GlobalTestInitialize and GlobalTestCleanup methods should have valid layout + GlobalTestInitialize 和 GlobalTestCleanup 方法应具有有效的布局 + + + + Methods like Contains, StartsWith, and EndsWith return boolean values that indicate whether the condition was met. Ignoring these return values is likely a mistake. + Contains、StartsWith 和 EndsWith 等方法会返回指示是否满足条件的布尔值。忽略这些返回值可能是一个错误。 + + + + The return value of '{0}' should not be ignored + 不应忽略 ‘{0}’ 的返回值 + + + + Do not ignore the return value of string methods + 不要忽略字符串方法的返回值 + + Use 'Assert.Fail' instead of an always-failing 'Assert.{0}' assert 使用 “Assert.Fail” 而不是始终失败的 “Assert.{0}” 断言 @@ -437,6 +547,21 @@ The type declaring these methods should also respect the following rules: 使用 “Assert.Fail” 而不是始终失败的断言 + + 'DataTestMethodAttribute' is obsolete and provides no additional functionality over 'TestMethodAttribute'. Use 'TestMethodAttribute' for all test methods, including parameterized tests. + 'DataTestMethodAttribute' 已过时,并且不再提供 'TestMethodAttribute' 之外的其他功能。对所有测试方法(包括参数化测试)使用 'TestMethodAttribute'。 + + + + 'DataTestMethod' is obsolete. Use 'TestMethod' instead. + 'DataTestMethod' 已过时。请改用 'TestMethod'。 + + + + Prefer 'TestMethod' over 'DataTestMethod' + 首选 'TestMethod' 而不是 'DataTestMethod' + + Review or remove the assertion as its condition is known to be always true 查看或移除断言,因为已知其条件始终为 true @@ -517,6 +642,16 @@ The type declaring these methods should also respect the following rules: 公共类型应为测试类 + + Use 'Assert.{0}' instead of 'StringAssert.{1}' + 使用 'Assert.{0}' 而不是 'StringAssert.{1}' + + + + Use 'Assert' instead of 'StringAssert' + 使用 'Assert' 而不是 'StringAssert' + + Test class '{0}' should be valid 测试类“{0}”应该有效 @@ -532,6 +667,21 @@ The type declaring these methods should also respect the following rules: 测试方法“{0}”签名无效 + + TestContext.CancellationToken provides a more direct way to access the cancellation token compared to TestContext.CancellationTokenSource.Token. + 与 TestContext.CancellationTokenSource.Token 相比,TestContext.CancellationToken 提供了更直接的方式来访问取消令牌。 + + + + Use 'TestContext.CancellationToken' instead of 'TestContext.CancellationTokenSource.Token' + 使用 'TestContext.CancellationToken' 而不是 'TestContext.CancellationTokenSource.Token' + + + + Use TestContext.CancellationToken instead of TestContext.CancellationTokenSource.Token + 使用 TestContext.CancellationToken 而不是 TestContext.CancellationTokenSource.Token + + Without using 'ClassCleanupBehavior.EndOfClass', the '[ClassCleanup]' will by default be run at the end of the assembly and not at the end of the class. 如果不使用 "ClassCleanupBehavior.EndOfClass","[ClassCleanup]" 将默认在程序集末尾运行,而不是在类的末尾运行。 @@ -720,13 +870,13 @@ The type declaring these methods should also respect the following rules: - Type contaning '[TestMethod]' should be marked with '[TestClass]', otherwise the test method will be silently ignored. - 应使用“[TestClass]”标记包含“[TestMethod]”的类型,否则将以静默方式忽略该测试方法。 + Type containing '[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 '{0}' contains test methods and should be marked with '[TestClass]' + 类型 "{0}" 包含测试方法,应使用 "[TestClass]" 进行标记 @@ -744,6 +894,11 @@ The type declaring these methods should also respect the following rules: 异步测试方法不需要 "Async" 后缀 + + TestContext parameter is required by MSTest for AssemblyInitialize and ClassInitialize methods + AssemblyInitialize 和 ClassInitialize 方法的 MSTest 需要 TestContext 参数 + + [{0}] can only be set on methods marked with [TestMethod] 只能对标记了 [TestMethod] 的方法设置 [{0}] @@ -764,6 +919,21 @@ The type declaring these methods should also respect the following rules: 对测试类使用 “ConditionBaseAttribute” + + Using '[Timeout]' without explicitly setting 'CooperativeCancellation = true' is discouraged. In a future version, cooperative cancellation will become the default behavior. Set 'CooperativeCancellation = true' to opt into the recommended behavior and avoid breaking changes. + 不建议在不显式设置 'CooperativeCancellation = true' 的情况下使用 '[Timeout]'。在将来的版本中,协作取消将成为默认行为。设置 'CooperativeCancellation = true' 以选择加入建议的行为,并避免中断性变更。 + + + + Use 'CooperativeCancellation = true' with '[Timeout]' to enable cooperative cancellation behavior + 将 'CooperativeCancellation = true' 与 '[Timeout]' 配合使用以启用协作取消行为 + + + + Use 'CooperativeCancellation = true' with '[Timeout]' + 将 'CooperativeCancellation = true' 与 '[Timeout]' 配合使用 + + '[DeploymentItem]' can be specified only on test class or test method 只能对测试类或测试方法指定 "[DeploymentItem]" @@ -819,6 +989,36 @@ The type declaring these methods should also respect the following rules: 对测试方法使用重试属性 + + TestContext property cannot be accessed in this context + 无法在此上下文中访问 TestContext 属性 + + + + TestContext property '{0}' cannot be accessed in '{1}' method + 无法在 ‘{1}’ 方法中访问 TestContext 属性 ‘{0}’ + + + + Some TestContext properties are only available during test execution and cannot be accessed in assembly initialize, class initialize, class cleanup, or assembly cleanup methods. + 一些 TestContext 属性仅在测试执行期间可用,并且无法在程序集初始化、类初始化、类清理或程序集清理方法中访问。 + + + + Assert.Throws methods should contain only a single statement or expression. Multiple statements can be misleading - if the first statement throws, subsequent statements are never executed; if it doesn't throw, it should be moved outside the Assert.Throws. + Assert.Throws 方法应只包含单个语句或表达式。多个语句可能会产生误导性 - 如果第一个语句引发,则永远不会执行后续语句;如果不引发,则应将其移到 Assert.Throws 外部。 + + + + Assert.Throws should contain only a single statement/expression + Assert.Throws 应仅包含单个语句/表达式 + + + + Assert.Throws should contain only a single statement/expression + Assert.Throws 应仅包含单个语句/表达式 + + \ No newline at end of file diff --git a/src/Analyzers/MSTest.Analyzers/xlf/Resources.zh-Hant.xlf b/src/Analyzers/MSTest.Analyzers/xlf/Resources.zh-Hant.xlf index dca44939e7..3b1c94232d 100644 --- a/src/Analyzers/MSTest.Analyzers/xlf/Resources.zh-Hant.xlf +++ b/src/Analyzers/MSTest.Analyzers/xlf/Resources.zh-Hant.xlf @@ -132,6 +132,16 @@ The type declaring these methods should also respect the following rules: 不要在值類型中使用 'Assert.AreSame' 或 'Assert.AreNotSame' + + Replace '{0}' with format parameters with string.Format or string interpolation + 將 '{0}' 取代為具有 string.Format 或字串內插補點的格式參數 + + + + Avoid using Assert methods with format parameters + 避免使用 Assert 方法搭配格式參數 + + Prefer 'Assert.ThrowsExactly' or 'Assert.ThrowsExactlyAsync' over '[ExpectedException]' as it ensures that only the expected call throws the expected exception. The assert APIs also provide more flexibility and allow you to assert extra properties of the exception. 偏好 'Assert.ThrowsExactly' 或 'Assert.ThrowsExactlyAsync' 而不是 '[ExpectedException]',因為它可確保只有預期的呼叫會擲出預期的例外狀況。判斷提示 API 也提供更多彈性,並允許您判斷提示例外狀況的額外屬性。 @@ -147,6 +157,16 @@ The type declaring these methods should also respect the following rules: 避免 '[ExpectedException]' + + Remove the 'DynamicDataSourceType' argument to use the default auto detect behavior + 移除 'DynamicDataSourceType' 引數,以使用預設的自動偵測行為 + + + + Avoid passing an explicit 'DynamicDataSourceType' and use the default auto detect behavior + 避免傳遞明確的 'DynamicDataSourceType',並使用預設的自動偵測行為 + + Do not assert inside 'async void' methods, local functions, or lambdas. Exceptions that are thrown in this context will be unhandled exceptions. When using VSTest under .NET Framework, they will be silently swallowed. When using Microsoft.Testing.Platform or VSTest under modern .NET, they may crash the process. 不要在 'async void' 方法、本機函數或 lambdas 內判斷提示。在此內容中擲回的例外狀況將會是未處理的例外狀況。在 .NET Framework 下使用 VSTest 時,會以無訊息方式接受。在現代 .NET 下使用 Microsoft.Testing.Platform 或 VSTest 時,可能會損毀進程。 @@ -277,8 +297,13 @@ The type declaring these methods should also respect the following rules: - DataRow argument type should match method parameter type. Mismatches occur at indices: {0} - DataRow 引數類型應符合方法參數類型。索引發生不符: {0} + DataRow argument types do not match method parameter types. {0} + DataRow 引數類型不符合方法參數類型。{0} + + + + Parameter '{0}' expects type '{1}', but the provided value has type '{2}' + 參數 '{0}' 期望類型為 '{1}',但提供的值的類型為 '{2}' @@ -402,11 +427,21 @@ The type declaring these methods should also respect the following rules: [DynamicData]' 只能在測試方法上設定 + + '[DynamicData]' member '{0}.{1}' is a field so you should use 'DynamicDataSourceType.AutoDetect' or 'DynamicDataSourceType.Field' (auto detect is the default when not specified explicitly, and is recommended) + '[DynamicData]' 成員 '{0}.{1}' 是欄位,因此您應該使用 'DynamicDataSourceType.AutoDetect' 或 'DynamicDataSourceType.Field' (未明確指定時,自動偵測為預設值,建議使用) + + '[DynamicData]' member '{0}.{1}' is a method so you should use 'DynamicDataSourceType.AutoDetect' or 'DynamicDataSourceType.Method' (auto detect is the default when not specified explicitly, and is recommended) '[DynamicData]' 成員 '{0}.{1}' 是方法,因此您應該使用 'DynamicDataSourceType.AutoDetect' 或 'DynamicDataSourceType.Method' (未明確指定時,自動偵測是預設值,建議) + + '[DynamicData]' member '{0}.{1}' is not a property, method, or field. Only properties, methods, and fields are supported. + '[DynamicData]' 成員 '{0}.{1}' 不是屬性、方法或欄位。僅支援屬性、方法和欄位。 + + '[DynamicData]' member '{0}.{1}' is not a property nor a method. Only properties and methods are supported. '[DynamicData]' 成員 『{0}.{1}』 不是屬性也不是方法。只支援屬性和方法。 @@ -427,6 +462,81 @@ The type declaring these methods should also respect the following rules: DynamicData 應該有效 + + When calling async methods that have overloads accepting a CancellationToken parameter, prefer using the overload with TestContext.CancellationToken to enable cooperative cancellation and respect test timeouts. + 在呼叫接受 CancellationToken 參數的多載非同步方法時,建議使用 TestContext.CancellationToken 的多載,以啟用共同取消並遵守測試逾時。 + + + + Consider using the overload that accepts a CancellationToken and pass 'TestContext.CancellationToken' + 考慮使用接受 CancellationToken 的多載,並傳遞 'TestContext.CancellationToken' + + + + Flow TestContext.CancellationToken to async operations + 將 TestContext.CancellationToken 傳遞給非同步作業 + + + + Methods marked with '[GlobalTestInitialize]' or '[GlobalTestCleanup]' should follow the following layout to be valid: +-it can't be declared on a generic class +-it should be 'public' +-it should be 'static' +-it should not be 'async void' +-it should not be a special method (finalizer, operator...). +-it should not be generic +-it should take one parameter of type 'TestContext' +-return type should be 'void', 'Task' or 'ValueTask' + +The type declaring these methods should also respect the following rules: +-The type should be a class +-The class should be 'public' +-The class shouldn't be 'static' +-The class should be marked with '[TestClass]' (or a derived attribute) +-the class should not be generic. + 標示為 '[GlobalTestInitialize]' 或 '[GlobalTestCleanup]' 的方法應該遵循下列配置才能有效: +-其不能在泛型類別上宣告 +-其應為 'public' +-其應為 'static' +-其不應為 'async void' +-其不應為特殊方法 (完成項、運算子...)。 +-其不應為泛型 +-其應該接受類型為 'TestContext' 的一個參數 +-傳回類型應為 'void'、'Task' 或 'ValueTask' + +宣告這些方法的類型還應遵循以下規則: +-類型應為類別 +-類別應為 'public' +-類別不應為 'static' +-類別應標示為 '[TestClass]' (或衍生屬性) +-類別不應為泛型。 + + + + Global test fixture method '{0}' signature is invalid + 全域測試固件方法 '{0}' 的簽名無效 + + + + GlobalTestInitialize and GlobalTestCleanup methods should have valid layout + GlobalTestInitialize 和 GlobalTestCleanup 方法應具備有效的配置 + + + + Methods like Contains, StartsWith, and EndsWith return boolean values that indicate whether the condition was met. Ignoring these return values is likely a mistake. + Contains、StartsWith 和 EndsWith 等方法會傳回布林值,指出是否已符合條件。忽略這些傳回值可能導致錯誤。 + + + + The return value of '{0}' should not be ignored + 不應忽略傳回值 '{0}' + + + + Do not ignore the return value of string methods + 請勿忽略字串方法的傳回值 + + Use 'Assert.Fail' instead of an always-failing 'Assert.{0}' assert 使用 'Assert.Fail',而不是一直失敗的 'Assert.{0}' 聲明 @@ -437,6 +547,21 @@ The type declaring these methods should also respect the following rules: 使用 'Assert.Fail',而不是一直失敗的聲明 + + 'DataTestMethodAttribute' is obsolete and provides no additional functionality over 'TestMethodAttribute'. Use 'TestMethodAttribute' for all test methods, including parameterized tests. + 'DataTestMethodAttribute' 已過時,未提供比 'TestMethodAttribute' 更多的功能。針對所有測試方法使用 'TestMethodAttribute',包括參數化測試。 + + + + 'DataTestMethod' is obsolete. Use 'TestMethod' instead. + 'DataTestMethod' 已過時。請改用 'TestMethod'。 + + + + Prefer 'TestMethod' over 'DataTestMethod' + 優先使用 'TestMethod' 而不是 'DataTestMethod' + + Review or remove the assertion as its condition is known to be always true 檢閱或移除判斷提示,因為已知其條件一律為 True @@ -517,6 +642,16 @@ The type declaring these methods should also respect the following rules: 公用類型應為測試類別 + + Use 'Assert.{0}' instead of 'StringAssert.{1}' + 使用 'Assert.{0}' 而不是 'StringAssert.{1}' + + + + Use 'Assert' instead of 'StringAssert' + 使用 'Assert' 而不是 'StringAssert' + + Test class '{0}' should be valid 測試類別 '{0}' 應為有效 @@ -532,6 +667,21 @@ The type declaring these methods should also respect the following rules: 測試方法 '{0}' 簽章無效 + + TestContext.CancellationToken provides a more direct way to access the cancellation token compared to TestContext.CancellationTokenSource.Token. + 相較於 TestContext.CancellationTokenSource.Token,TestContext.CancellationToken 提供存取取消權杖更直接的方法。 + + + + Use 'TestContext.CancellationToken' instead of 'TestContext.CancellationTokenSource.Token' + 使用 'TestContext.CancellationToken' 而不是 'TestContext.CancellationTokenSource.Token' + + + + Use TestContext.CancellationToken instead of TestContext.CancellationTokenSource.Token + 使用 TestContext.CancellationToken 而不是 TestContext.CancellationTokenSource.Token + + Without using 'ClassCleanupBehavior.EndOfClass', the '[ClassCleanup]' will by default be run at the end of the assembly and not at the end of the class. 如不使用 'ClassCleanupBehavior.EndOfClass',則預設會在組件結尾執行 '[ClassCleanup]',而非在類別的結尾執行。 @@ -720,13 +870,13 @@ The type declaring these methods should also respect the following rules: - Type contaning '[TestMethod]' should be marked with '[TestClass]', otherwise the test method will be silently ignored. + Type containing '[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 '{0}' contains test methods and should be marked with '[TestClass]' + 類型 '{0}' 包含測試方法,應以 '[TestClass]' 標記 @@ -744,6 +894,11 @@ The type declaring these methods should also respect the following rules: 非同步測試方法不需要 'Async' 尾碼 + + TestContext parameter is required by MSTest for AssemblyInitialize and ClassInitialize methods + MSTest 需要 TestContext 參數,以用於 AssemblyInitialize 和 ClassInitialize 方法 + + [{0}] can only be set on methods marked with [TestMethod] [{0}] 只能在標示為 [TestMethod] 的方法上設定 @@ -764,6 +919,21 @@ The type declaring these methods should also respect the following rules: 在測試類別上使用 'ConditionBaseAttribute' + + Using '[Timeout]' without explicitly setting 'CooperativeCancellation = true' is discouraged. In a future version, cooperative cancellation will become the default behavior. Set 'CooperativeCancellation = true' to opt into the recommended behavior and avoid breaking changes. + 在未明確設定 'CooperativeCancellation = true' 的情況下,不建議使用 '[Timeout]'。在未來的版本中,合作取消將成為預設行為。請設定 'CooperativeCancellation = true' 以選擇加入推薦的行為,並避免破壞性變更。 + + + + Use 'CooperativeCancellation = true' with '[Timeout]' to enable cooperative cancellation behavior + 使用 'CooperativeCancellation = true' 搭配 '[Timeout]' 以啟用合作取消行為 + + + + Use 'CooperativeCancellation = true' with '[Timeout]' + 使用 'CellCancellation = true' 搭配 '[Timeout]' + + '[DeploymentItem]' can be specified only on test class or test method '[DeploymentItem]' 只能在測試類或測試方法上指定 @@ -819,6 +989,36 @@ The type declaring these methods should also respect the following rules: 在測試方法上使用重試屬性 + + TestContext property cannot be accessed in this context + 無法在此內容中存取 TestContext 屬性 + + + + TestContext property '{0}' cannot be accessed in '{1}' method + 無法在 '{1}' 方法中存取 TestContext 屬性 '{0}' + + + + Some TestContext properties are only available during test execution and cannot be accessed in assembly initialize, class initialize, class cleanup, or assembly cleanup methods. + 某些 TestContext 屬性僅在測試執行期間可供使用,無法在組件初始化、類別初始化、類別清理或組件清理方法中存取。 + + + + Assert.Throws methods should contain only a single statement or expression. Multiple statements can be misleading - if the first statement throws, subsequent statements are never executed; if it doesn't throw, it should be moved outside the Assert.Throws. + Assert.Throws 方法應該只包含單一陳述式或運算式。多個陳述式可能會產生誤導 - 如果擲出第一個陳述式,後續陳述式永遠不會執行; 如果未擲回,則應該移到 Assert.Throws 之外。 + + + + Assert.Throws should contain only a single statement/expression + Assert.Throws 應該只包含單一陳述式/運算式 + + + + Assert.Throws should contain only a single statement/expression + Assert.Throws 應該只包含單一陳述式/運算式 + + \ No newline at end of file diff --git a/src/Analyzers/MSTest.GlobalConfigsGenerator/AnalyzerSeverityDecider.cs b/src/Analyzers/MSTest.GlobalConfigsGenerator/AnalyzerSeverityDecider.cs index adf182083e..ac1370ab38 100644 --- a/src/Analyzers/MSTest.GlobalConfigsGenerator/AnalyzerSeverityDecider.cs +++ b/src/Analyzers/MSTest.GlobalConfigsGenerator/AnalyzerSeverityDecider.cs @@ -86,7 +86,8 @@ internal static class AnalyzerSeverityDecider if (rule.IsEnabledByDefault && rule.DefaultSeverity >= DiagnosticSeverity.Info) { // Recommended mode will elevate info to warning only if the rule is enabled by default. - return DiagnosticSeverity.Warning; + // In addition, if the rule is already error by default, we keep it as error. So, choose the max between warning and default severity. + return (DiagnosticSeverity)Math.Max((int)rule.DefaultSeverity, (int)DiagnosticSeverity.Warning); } if (rule.DefaultSeverity >= DiagnosticSeverity.Warning) @@ -117,6 +118,8 @@ internal static class AnalyzerSeverityDecider return rule.IsEnabledByDefault ? rule.DefaultSeverity : null; } - private static DiagnosticSeverity? DecideForModeNone(DiagnosticDescriptor _) - => null; + private static DiagnosticSeverity? DecideForModeNone(DiagnosticDescriptor rule) + // Even with 'None' mode, we still keep the rules that are errors by default. + // Such rules are likely to be critical and shouldn't be suppressed by MSTestAnalysisMode None. + => rule.DefaultSeverity == DiagnosticSeverity.Error ? DiagnosticSeverity.Error : null; } diff --git a/src/Analyzers/MSTest.GlobalConfigsGenerator/GlobalConfigBuilder.cs b/src/Analyzers/MSTest.GlobalConfigsGenerator/GlobalConfigBuilder.cs index d0940b34f5..9eb780c850 100644 --- a/src/Analyzers/MSTest.GlobalConfigsGenerator/GlobalConfigBuilder.cs +++ b/src/Analyzers/MSTest.GlobalConfigsGenerator/GlobalConfigBuilder.cs @@ -6,7 +6,7 @@ internal sealed class GlobalConfigBuilder { private readonly StringBuilder _builder = new(); - private readonly Dictionary _severityDictionary = new(); + private readonly Dictionary _severityDictionary = []; public GlobalConfigBuilder(MSTestAnalysisMode mode) { diff --git a/src/Analyzers/MSTest.SourceGeneration/Generators/TestNodesGenerator.cs b/src/Analyzers/MSTest.SourceGeneration/Generators/TestNodesGenerator.cs index c841f5d90d..a1ca1ed89c 100644 --- a/src/Analyzers/MSTest.SourceGeneration/Generators/TestNodesGenerator.cs +++ b/src/Analyzers/MSTest.SourceGeneration/Generators/TestNodesGenerator.cs @@ -50,10 +50,9 @@ private static void AddAssemblyTestNode(SourceProductionContext context, (string sourceStringBuilder.AppendAutoGeneratedHeader(); sourceStringBuilder.AppendLine(); - TestNamespaceInfo[] uniqueUsedNamespaces = testClasses + TestNamespaceInfo[] uniqueUsedNamespaces = [.. testClasses .Select(x => x.ContainingNamespace) - .Distinct() - .ToArray(); + .Distinct()]; string? safeAssemblyName = null; IDisposable? namespaceBlock = null; @@ -148,7 +147,7 @@ private static void AddAssemblyTestNode(SourceProductionContext context, (string private static void AppendAssemblyTestNodeBuilderContent(IndentedStringBuilder sourceStringBuilder, string assemblyName, ImmutableArray testClasses) { - Dictionary rootVariablesPerNamespace = new(); + Dictionary rootVariablesPerNamespace = []; int variableIndex = 1; IEnumerable> classesPerNamespaces = testClasses.GroupBy(x => x.ContainingNamespace); foreach (IGrouping namespaceClasses in classesPerNamespaces) @@ -159,7 +158,6 @@ private static void AppendAssemblyTestNodeBuilderContent(IndentedStringBuilder s foreach (TestTypeInfo testClassInfo in namespaceClasses) { - string escapedClassFullName = TestNodeHelpers.GenerateEscapedName(testClassInfo.FullyQualifiedName); sourceStringBuilder.AppendLine($"{namespaceTestsVariableName}.Add({testClassInfo.GeneratedTypeName}.TestNode);"); } @@ -169,7 +167,7 @@ private static void AppendAssemblyTestNodeBuilderContent(IndentedStringBuilder s sourceStringBuilder.Append("MSTF::TestNode root = "); - using (sourceStringBuilder.AppendTestNode(assemblyName, assemblyName, Array.Empty(), ';')) + using (sourceStringBuilder.AppendTestNode(assemblyName, assemblyName, [], ';')) { foreach (IGrouping group in classesPerNamespaces) { diff --git a/src/Analyzers/MSTest.SourceGeneration/Helpers/IndentedStringBuilder.cs b/src/Analyzers/MSTest.SourceGeneration/Helpers/IndentedStringBuilder.cs index 39fdb20ba0..a9b2c407f4 100644 --- a/src/Analyzers/MSTest.SourceGeneration/Helpers/IndentedStringBuilder.cs +++ b/src/Analyzers/MSTest.SourceGeneration/Helpers/IndentedStringBuilder.cs @@ -76,7 +76,7 @@ private StringBuilder MaybeAppendIndent() { if (_needsIndent) { - _builder.Append(new string(' ', IndentationLevel * 4)); + _builder.Append(' ', IndentationLevel * 4); } return _builder; diff --git a/src/Analyzers/MSTest.SourceGeneration/Helpers/SystemPolyfills.cs b/src/Analyzers/MSTest.SourceGeneration/Helpers/SystemPolyfills.cs index 7a3645233e..6a80a1ca13 100644 --- a/src/Analyzers/MSTest.SourceGeneration/Helpers/SystemPolyfills.cs +++ b/src/Analyzers/MSTest.SourceGeneration/Helpers/SystemPolyfills.cs @@ -3,62 +3,21 @@ #if !NETCOREAPP #pragma warning disable SA1403 // File may only contain a single namespace -#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 SA1502 // Element should not be on a single line +#pragma warning disable SA1623 // Property summary documentation should match accessors using System.ComponentModel; namespace System.Runtime.CompilerServices { [EditorBrowsable(EditorBrowsableState.Never)] - internal static class IsExternalInit { } + internal static class IsExternalInit; } // 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 { } - - /// 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 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 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 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 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; } - } - /// 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 @@ -72,127 +31,6 @@ internal sealed class NotNullWhenAttribute : Attribute /// Gets the return value condition. public bool ReturnValue { 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 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 - { - } -} - -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 - { - /// - /// Creates a new instance of the type. - /// - /// The name of the feature to indicate. - public CompilerFeatureRequiredAttribute(string featureName) => FeatureName = featureName; - - /// - /// The name of the compiler feature. - /// - public string FeatureName { get; } - - /// - /// 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; } - - /// - /// 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); - } - - /// - /// 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/Analyzers/MSTest.SourceGeneration/MSTest.SourceGeneration.csproj b/src/Analyzers/MSTest.SourceGeneration/MSTest.SourceGeneration.csproj index af7c69027c..3eb6317ab6 100644 --- a/src/Analyzers/MSTest.SourceGeneration/MSTest.SourceGeneration.csproj +++ b/src/Analyzers/MSTest.SourceGeneration/MSTest.SourceGeneration.csproj @@ -3,8 +3,6 @@ netstandard2.0 - - true false NU5128 @@ -41,7 +39,7 @@ This package provides the C# source generators for MSTest test framework.]]> - + diff --git a/src/Analyzers/MSTest.SourceGeneration/ObjectModels/DataRowTestMethodArgumentsInfo.cs b/src/Analyzers/MSTest.SourceGeneration/ObjectModels/DataRowTestMethodArgumentsInfo.cs index 5380802d1d..7bc9a24b18 100644 --- a/src/Analyzers/MSTest.SourceGeneration/ObjectModels/DataRowTestMethodArgumentsInfo.cs +++ b/src/Analyzers/MSTest.SourceGeneration/ObjectModels/DataRowTestMethodArgumentsInfo.cs @@ -14,7 +14,7 @@ internal sealed class DataRowTestMethodArgumentsInfo : ITestMethodArgumentsInfo private readonly ImmutableArray> _argumentsRows; private readonly TestMethodParametersInfo _parametersInfo; - public bool IsTestArgumentsEntryReturnType { get; } = true; + public bool IsTestArgumentsEntryReturnType => true; public string? GeneratorMethodFullName { get; } @@ -43,7 +43,7 @@ public void AppendArguments(IndentedStringBuilder nodeBuilder) string argumentsEntry = arguments.Length > 1 ? "(" + string.Join(", ", arguments) + ")" : arguments[0]; - string argumentsUid = GetArgumentsUid(_parametersInfo.Parameters.Select(x => x.Name).ToArray(), arguments); + string argumentsUid = GetArgumentsUid([.. _parametersInfo.Parameters.Select(x => x.Name)], arguments); nodeBuilder.AppendLine($"new {TestMethodInfo.TestArgumentsEntryTypeName}<{_parametersInfo.ParametersTuple}>({argumentsEntry}, \"{argumentsUid}\"),"); } } diff --git a/src/Analyzers/MSTest.SourceGeneration/ObjectModels/DynamicDataTestMethodArgumentsInfo.cs b/src/Analyzers/MSTest.SourceGeneration/ObjectModels/DynamicDataTestMethodArgumentsInfo.cs index e40db92762..0d08f672a8 100644 --- a/src/Analyzers/MSTest.SourceGeneration/ObjectModels/DynamicDataTestMethodArgumentsInfo.cs +++ b/src/Analyzers/MSTest.SourceGeneration/ObjectModels/DynamicDataTestMethodArgumentsInfo.cs @@ -19,6 +19,7 @@ internal sealed class DynamicDataTestMethodArgumentsInfo : ITestMethodArgumentsI private const int DynamicDataSourceTypeProperty = 0; private const int DynamicDataSourceTypeMethod = 1; private const int DynamicDataSourceTypeAutoDetect = 2; + private const int DynamicDataSourceTypeField = 3; internal const string TestArgumentsEntryTypeName = "MSTF::InternalUnsafeTestArgumentsEntry"; internal const string DynamicDataNameProviderTypeName = "MSTF::DynamicDataNameProvider"; @@ -94,6 +95,7 @@ private DynamicDataTestMethodArgumentsInfo(string memberName, string memberFullT { DynamicDataSourceTypeProperty => SymbolKind.Property, DynamicDataSourceTypeMethod => SymbolKind.Method, + DynamicDataSourceTypeField => SymbolKind.Field, DynamicDataSourceTypeAutoDetect => null, _ => throw ApplicationStateGuard.Unreachable(), }; @@ -102,33 +104,31 @@ private DynamicDataTestMethodArgumentsInfo(string memberName, string memberFullT private static DynamicDataTestMethodArgumentsInfo? TryBuildFromDynamicData(INamedTypeSymbol memberTypeSymbol, string memberName, SymbolKind? symbolKind, WellKnownTypes wellKnownTypes, IMethodSymbol testMethodSymbol) { - // Dynamic data only support Properties and Methods, but not fields. + // Dynamic data supports Properties, Methods, and Fields. // null is also possible and means "AutoDetect" - if (symbolKind is not (SymbolKind.Property or SymbolKind.Method or null)) + if (symbolKind is not (SymbolKind.Property or SymbolKind.Method or SymbolKind.Field or null)) { return null; } ISymbol? firstMatchingMember = memberTypeSymbol.GetAllMembers(memberName) .SelectMany(x => x) - // DynamicData does not have option to not specify the kind of member we are looking for at the moment. - // But the code below can easily handle searching for all kinds of supported members, and only - // take the kind into consideration when needed. If a ctor is added to DynamicData, or if we can tell if - // user specified the value explicitly or if it was taken from the default value, then `symbolKind` can be made nullable - // and only filtered below. + // DynamicData supports properties, methods, and fields. // .Where(s => s.IsStatic && (s.Kind is SymbolKind.Field or SymbolKind.Property or SymbolKind.Method)) // .Where(s => symbolKind == null || s.Kind == symbolKind) - .Where(s => s.IsStatic && (s.Kind == symbolKind || (symbolKind is null && s.Kind is SymbolKind.Property or SymbolKind.Method))) + .Where(s => s.IsStatic && (s.Kind == symbolKind || (symbolKind is null && s.Kind is SymbolKind.Property or SymbolKind.Method or SymbolKind.Field))) .Select(s => s switch { IPropertySymbol propertySymbol => (ISymbol)propertySymbol, IMethodSymbol methodSymbol => methodSymbol, + IFieldSymbol fieldSymbol => fieldSymbol, _ => throw ApplicationStateGuard.Unreachable(), }) .OrderBy(tuple => tuple.Kind switch { SymbolKind.Property => 1, SymbolKind.Method => 2, + SymbolKind.Field => 3, _ => throw ApplicationStateGuard.Unreachable(), }) .FirstOrDefault(); @@ -205,7 +205,7 @@ public void AppendArguments(IndentedStringBuilder nodeBuilder) nodeBuilder.Append("()"); } - nodeBuilder.AppendLine(";"); + nodeBuilder.AppendLine(';'); if (!_targetMethodReturnsCollectionOfTestArgumentsEntry) { @@ -225,6 +225,6 @@ public void AppendArguments(IndentedStringBuilder nodeBuilder) } } - nodeBuilder.AppendLine(","); + nodeBuilder.AppendLine(','); } } diff --git a/src/Analyzers/MSTest.SourceGeneration/ObjectModels/TestMethodInfo.cs b/src/Analyzers/MSTest.SourceGeneration/ObjectModels/TestMethodInfo.cs index 4cad071a97..708dce9b17 100644 --- a/src/Analyzers/MSTest.SourceGeneration/ObjectModels/TestMethodInfo.cs +++ b/src/Analyzers/MSTest.SourceGeneration/ObjectModels/TestMethodInfo.cs @@ -89,10 +89,10 @@ private TestMethodInfo(IMethodSymbol methodSymbol, INamedTypeSymbol typeUsingMet return null; } - List dataRowAttributes = new(); - List dynamicDataAttributes = new(); - List testPropertyAttributes = new(); - List<(string RuleId, string Description)> pragmas = new(); + List dataRowAttributes = []; + List dynamicDataAttributes = []; + List testPropertyAttributes = []; + List<(string RuleId, string Description)> pragmas = []; TimeSpan? testExecutionTimeout = null; foreach (AttributeData attribute in attributes) { diff --git a/src/Analyzers/MSTest.SourceGeneration/ObjectModels/TestNamespaceInfo.cs b/src/Analyzers/MSTest.SourceGeneration/ObjectModels/TestNamespaceInfo.cs index a17a31fc11..308a04ebea 100644 --- a/src/Analyzers/MSTest.SourceGeneration/ObjectModels/TestNamespaceInfo.cs +++ b/src/Analyzers/MSTest.SourceGeneration/ObjectModels/TestNamespaceInfo.cs @@ -30,8 +30,7 @@ public TestNamespaceInfo(INamespaceSymbol namespaceSymbol) public void AppendNamespaceTestNode(IndentedStringBuilder nodeStringBuilder, string testsVariableName) { - using (nodeStringBuilder.AppendTestNode(_containingAssembly + "." + _nameOrGlobalNamespace, _nameOrGlobalNamespace, - Array.Empty(), testsVariableName)) + using (nodeStringBuilder.AppendTestNode(_containingAssembly + "." + _nameOrGlobalNamespace, _nameOrGlobalNamespace, [], testsVariableName)) { } } diff --git a/src/Analyzers/MSTest.SourceGeneration/ObjectModels/TestTypeInfo.cs b/src/Analyzers/MSTest.SourceGeneration/ObjectModels/TestTypeInfo.cs index 60d46150ed..c1ec3a610e 100644 --- a/src/Analyzers/MSTest.SourceGeneration/ObjectModels/TestTypeInfo.cs +++ b/src/Analyzers/MSTest.SourceGeneration/ObjectModels/TestTypeInfo.cs @@ -156,7 +156,7 @@ public void AppendTestNode(IndentedStringBuilder sourceStringBuilder) private void AppendTestNodeCreation(IndentedStringBuilder sourceStringBuilder) { - List properties = new(); + List properties = []; foreach ((string filePath, int startLine, int endLine) in _declarationReferences) { properties.Add($"new Msg::TestFileLocationProperty(@\"{filePath}\", new(new({startLine}, -1), new({endLine}, -1))),"); diff --git a/src/Package/MSTest.Sdk/Sdk/Runner/ClassicEngine.targets b/src/Package/MSTest.Sdk/Sdk/Runner/ClassicEngine.targets index 0ba5bac6c4..8ff884ae40 100644 --- a/src/Package/MSTest.Sdk/Sdk/Runner/ClassicEngine.targets +++ b/src/Package/MSTest.Sdk/Sdk/Runner/ClassicEngine.targets @@ -34,6 +34,10 @@ true $(MicrosoftTestingExtensionsCommonVersion) + + true + $(MicrosoftTestingExtensionsCommonVersion) + true $(MicrosoftTestingExtensionsFakesVersion) @@ -48,6 +52,7 @@ - - - - diff --git a/src/Package/MSTest.Sdk/Sdk/Sdk.props.template b/src/Package/MSTest.Sdk/Sdk/Sdk.props.template index 2c6fe5ca7d..73530c9e0d 100644 --- a/src/Package/MSTest.Sdk/Sdk/Sdk.props.template +++ b/src/Package/MSTest.Sdk/Sdk/Sdk.props.template @@ -12,6 +12,8 @@ + + false true diff --git a/src/Package/MSTest/MSTest.csproj b/src/Package/MSTest/MSTest.csproj index ecbba42c9e..52763d8709 100644 --- a/src/Package/MSTest/MSTest.csproj +++ b/src/Package/MSTest/MSTest.csproj @@ -46,7 +46,6 @@ - diff --git a/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/AzureDevOpsReporter.cs b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/AzureDevOpsReporter.cs index 9e7093ba63..5d9c0f65b3 100644 --- a/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/AzureDevOpsReporter.cs +++ b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/AzureDevOpsReporter.cs @@ -11,6 +11,7 @@ using Microsoft.Testing.Platform.Helpers; using Microsoft.Testing.Platform.Logging; using Microsoft.Testing.Platform.OutputDevice; +using Microsoft.Testing.TestInfrastructure; namespace Microsoft.Testing.Extensions.AzureDevOpsReport; @@ -23,7 +24,7 @@ internal sealed class AzureDevOpsReporter : private readonly IOutputDevice _outputDisplay; private readonly ILogger _logger; - private static readonly char[] NewlineCharacters = new char[] { '\r', '\n' }; + private static readonly char[] NewlineCharacters = ['\r', '\n']; private readonly ICommandLineOptions _commandLine; private readonly IEnvironment _environment; private readonly IFileSystem _fileSystem; @@ -51,10 +52,10 @@ public AzureDevOpsReporter( public Type[] DataTypesProduced { get; } = [typeof(SessionFileArtifact)]; /// - public string Uid { get; } = nameof(AzureDevOpsReporter); + public string Uid => nameof(AzureDevOpsReporter); /// - public string Version { get; } = AppVersion.DefaultSemVer; + public string Version => AppVersion.DefaultSemVer; /// public string DisplayName { get; } = AzureDevOpsResources.DisplayName; @@ -124,16 +125,16 @@ public async Task ConsumeAsync(IDataProducer dataProducer, IData value, Cancella switch (nodeState) { case FailedTestNodeStateProperty failed: - await WriteExceptionAsync(failed.Explanation, failed.Exception); + await WriteExceptionAsync(failed.Explanation, failed.Exception).ConfigureAwait(false); break; case ErrorTestNodeStateProperty error: - await WriteExceptionAsync(error.Explanation, error.Exception); + await WriteExceptionAsync(error.Explanation, error.Exception).ConfigureAwait(false); break; case CancelledTestNodeStateProperty cancelled: - await WriteExceptionAsync(cancelled.Explanation, cancelled.Exception); + await WriteExceptionAsync(cancelled.Explanation, cancelled.Exception).ConfigureAwait(false); break; case TimeoutTestNodeStateProperty timeout: - await WriteExceptionAsync(timeout.Explanation, timeout.Exception); + await WriteExceptionAsync(timeout.Explanation, timeout.Exception).ConfigureAwait(false); break; } } @@ -161,7 +162,7 @@ private async Task WriteExceptionAsync(string? explanation, Exception? exception _logger.LogTrace($"Showing failure message '{line}'."); } - await _outputDisplay.DisplayAsync(this, new FormattedTextOutputDeviceData(line)); + await _outputDisplay.DisplayAsync(this, new FormattedTextOutputDeviceData(line)).ConfigureAwait(false); } internal static /* for testing */ string? GetErrorText(string? explanation, Exception? exception, string severity, IFileSystem fileSystem, ILogger logger) @@ -263,7 +264,7 @@ private async Task WriteExceptionAsync(string? explanation, Exception? exception // Combine with repo root, to be able to resolve deterministic build paths. string fullPath = Path.Combine(repoRoot, relativePath); - if (!fileSystem.Exists(fullPath)) + if (!fileSystem.ExistFile(fullPath)) { // Path does not belong to current repository or does not exist, no need to report it because it will not show up in the PR error, we will only see it details of the run, which is the same // as not reporting it this way. Maybe there can be 2 modes, but right now we want this to be usable for GitHub + AzDo, not for pure AzDo. diff --git a/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/BannedSymbols.txt b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/BannedSymbols.txt index ea8617fcb0..09d715c49c 100644 --- a/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/BannedSymbols.txt +++ b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/BannedSymbols.txt @@ -6,5 +6,5 @@ M:System.Threading.Tasks.Task.WhenAll(System.Threading.Tasks.Task[]); Use 'ITask 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); 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.AzureDevOpsReport/Microsoft.Testing.Extensions.AzureDevOpsReport.csproj b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Microsoft.Testing.Extensions.AzureDevOpsReport.csproj index c509126613..02e9d786da 100644 --- a/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Microsoft.Testing.Extensions.AzureDevOpsReport.csproj +++ b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Microsoft.Testing.Extensions.AzureDevOpsReport.csproj @@ -2,10 +2,6 @@ netstandard2.0;$(MicrosoftTestingTargetFrameworks) - - - 1.0.0 - alpha @@ -39,11 +35,11 @@ This package extends Microsoft Testing Platform to provide a Azure DevOps report - + - + diff --git a/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/PublicAPI/PublicAPI.Shipped.txt b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/PublicAPI/PublicAPI.Shipped.txt index 7dc5c58110..3b7a744721 100644 --- a/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/PublicAPI/PublicAPI.Shipped.txt +++ b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/PublicAPI/PublicAPI.Shipped.txt @@ -1 +1,5 @@ #nullable enable +Microsoft.Testing.Extensions.AzureDevOpsExtensions +Microsoft.Testing.Extensions.AzureDevOpsReport.TestingPlatformBuilderHook +static Microsoft.Testing.Extensions.AzureDevOpsExtensions.AddAzureDevOpsProvider(this Microsoft.Testing.Platform.Builder.ITestApplicationBuilder! builder) -> void +static Microsoft.Testing.Extensions.AzureDevOpsReport.TestingPlatformBuilderHook.AddExtensions(Microsoft.Testing.Platform.Builder.ITestApplicationBuilder! testApplicationBuilder, string![]! _) -> void diff --git a/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/PublicAPI/PublicAPI.Unshipped.txt b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/PublicAPI/PublicAPI.Unshipped.txt index 2ad77b868e..7dc5c58110 100644 --- a/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/PublicAPI/PublicAPI.Unshipped.txt +++ b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/PublicAPI/PublicAPI.Unshipped.txt @@ -1,5 +1 @@ #nullable enable -Microsoft.Testing.Extensions.AzureDevOpsReport.TestingPlatformBuilderHook -Microsoft.Testing.Extensions.AzureDevOpsExtensions -static Microsoft.Testing.Extensions.AzureDevOpsReport.TestingPlatformBuilderHook.AddExtensions(Microsoft.Testing.Platform.Builder.ITestApplicationBuilder! testApplicationBuilder, string![]! _) -> void -static Microsoft.Testing.Extensions.AzureDevOpsExtensions.AddAzureDevOpsProvider(this Microsoft.Testing.Platform.Builder.ITestApplicationBuilder! builder) -> void diff --git a/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/AzureDevOpsResources.resx b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/AzureDevOpsResources.resx index cc27c6c8b4..516d5d4382 100644 --- a/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/AzureDevOpsResources.resx +++ b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/AzureDevOpsResources.resx @@ -134,6 +134,6 @@ Severity to use for the reported event. Options are: error (default) and warning. - Do not translated 'error' or 'warning' those are literal values. + {Locked="error"}{Locked="warning"} \ No newline at end of file diff --git a/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.cs.xlf b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.cs.xlf index 4689eb6b49..076a3037b9 100644 --- a/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.cs.xlf +++ b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.cs.xlf @@ -29,8 +29,8 @@ Severity to use for the reported event. Options are: error (default) and warning. - Závažnost, která se má použít pro hlášenou událost. Možnosti: chyba (výchozí) a upozornění. - Do not translated 'error' or 'warning' those are literal values. + Závažnost, která se má použít pro hlášenou událost. Možnosti: error (výchozí) a warning. + {Locked="error"}{Locked="warning"} diff --git a/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.de.xlf b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.de.xlf index d86ec79035..87be6a6ce5 100644 --- a/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.de.xlf +++ b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.de.xlf @@ -29,8 +29,8 @@ Severity to use for the reported event. Options are: error (default) and warning. - Schweregrad, der für das gemeldete Ereignis verwendet werden soll. Optionen sind: Fehler (Standard) und Warnung. - Do not translated 'error' or 'warning' those are literal values. + Schweregrad, der für das gemeldete Ereignis verwendet werden soll. Optionen sind: error (Standard) und warning. + {Locked="error"}{Locked="warning"} diff --git a/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.es.xlf b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.es.xlf index 8af2f91123..c4a61c952f 100644 --- a/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.es.xlf +++ b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.es.xlf @@ -29,8 +29,8 @@ Severity to use for the reported event. Options are: error (default) and warning. - Gravedad que se va a usar para el evento notificado. Las opciones son: error (valor predeterminado) y advertencia. - Do not translated 'error' or 'warning' those are literal values. + Gravedad que se va a usar para el evento notificado. Las opciones son: error (valor predeterminado) y warning. + {Locked="error"}{Locked="warning"} diff --git a/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.fr.xlf b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.fr.xlf index 42e2e1d99d..4feb6d0ab7 100644 --- a/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.fr.xlf +++ b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.fr.xlf @@ -29,8 +29,8 @@ Severity to use for the reported event. Options are: error (default) and warning. - Gravité à utiliser pour l’événement signalé. Les options sont : erreur (par défaut) et avertissement. - Do not translated 'error' or 'warning' those are literal values. + Gravité à utiliser pour l’événement signalé. Les options sont : error (par défaut) et warning. + {Locked="error"}{Locked="warning"} diff --git a/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.it.xlf b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.it.xlf index 2e778e1102..d9756f675c 100644 --- a/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.it.xlf +++ b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.it.xlf @@ -29,8 +29,8 @@ Severity to use for the reported event. Options are: error (default) and warning. - Gravità da utilizzare per l'evento segnalato. Le opzioni sono: errore (impostazione predefinita) e avviso. - Do not translated 'error' or 'warning' those are literal values. + Gravità da usare per l'evento segnalato. Le opzioni sono: error (impostazione predefinita) e warning. + {Locked="error"}{Locked="warning"} diff --git a/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.ja.xlf b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.ja.xlf index 8bfb6808d3..ea3529945b 100644 --- a/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.ja.xlf +++ b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.ja.xlf @@ -29,8 +29,8 @@ Severity to use for the reported event. Options are: error (default) and warning. - 報告されたイベントに使用する重大度。オプションは、エラー (既定) と警告です。 - Do not translated 'error' or 'warning' those are literal values. + 報告されたイベントに使用する重大度。オプションは、error (既定) と warning です。 + {Locked="error"}{Locked="warning"} diff --git a/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.ko.xlf b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.ko.xlf index 9c7063b64c..f74caa7f6e 100644 --- a/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.ko.xlf +++ b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.ko.xlf @@ -29,8 +29,8 @@ Severity to use for the reported event. Options are: error (default) and warning. - 보고된 이벤트에 사용할 심각도입니다. 옵션은 오류(기본값)와 경고, 두 가지입니다. - Do not translated 'error' or 'warning' those are literal values. + 보고된 이벤트에 사용할 심각도입니다. 옵션은 error(기본값)와 warning, 두 가지입니다. + {Locked="error"}{Locked="warning"} diff --git a/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.pl.xlf b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.pl.xlf index 6bd1122a9d..8890f27f70 100644 --- a/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.pl.xlf +++ b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.pl.xlf @@ -29,8 +29,8 @@ Severity to use for the reported event. Options are: error (default) and warning. - Ważność do użycia dla zgłoszonego zdarzenia. Dostępne opcje to: błąd (domyślny) i ostrzeżenie. - Do not translated 'error' or 'warning' those are literal values. + Ważność do użycia dla zgłoszonego zdarzenia. Dostępne opcje to: error (domyślny) i warning. + {Locked="error"}{Locked="warning"} diff --git a/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.pt-BR.xlf b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.pt-BR.xlf index ca51be8c38..d660d0e56a 100644 --- a/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.pt-BR.xlf +++ b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.pt-BR.xlf @@ -29,8 +29,8 @@ Severity to use for the reported event. Options are: error (default) and warning. - A gravidade que será usada para o evento relatado. As opções são: erro (padrão) e aviso. - Do not translated 'error' or 'warning' those are literal values. + A gravidade que será usada para o evento relatado. As opções são: error (padrão) e warning. + {Locked="error"}{Locked="warning"} diff --git a/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.ru.xlf b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.ru.xlf index 994718f04e..756d7c9cae 100644 --- a/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.ru.xlf +++ b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.ru.xlf @@ -30,7 +30,7 @@ Severity to use for the reported event. Options are: error (default) and warning. Уровень серьезности, используемый для события из отчета. Параметры: error (по умолчанию) и warning. - Do not translated 'error' or 'warning' those are literal values. + {Locked="error"}{Locked="warning"} diff --git a/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.tr.xlf b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.tr.xlf index 727dc67065..69207860a2 100644 --- a/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.tr.xlf +++ b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.tr.xlf @@ -29,8 +29,8 @@ Severity to use for the reported event. Options are: error (default) and warning. - Raporlanan olay için kullanılacak önem derecesi. Seçenekler şunlardır: hata (varsayılan) ve uyarı. - Do not translated 'error' or 'warning' those are literal values. + Raporlanan olay için kullanılacak önem derecesi. Seçenekler şunlardır: error (varsayılan) ve warning. + {Locked="error"}{Locked="warning"} diff --git a/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.zh-Hans.xlf b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.zh-Hans.xlf index 3f3b35566e..2b5199f4ad 100644 --- a/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.zh-Hans.xlf +++ b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.zh-Hans.xlf @@ -30,7 +30,7 @@ Severity to use for the reported event. Options are: error (default) and warning. 要用于所报告事件的严重性。选项为: error (默认)和 warning。 - Do not translated 'error' or 'warning' those are literal values. + {Locked="error"}{Locked="warning"} diff --git a/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.zh-Hant.xlf b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.zh-Hant.xlf index 5b3ebbf244..6c61f0fb8f 100644 --- a/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.zh-Hant.xlf +++ b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.zh-Hant.xlf @@ -29,8 +29,8 @@ Severity to use for the reported event. Options are: error (default) and warning. - 要用於所報告事件的嚴重性。選項為: 錯誤 (預設) 警告。 - Do not translated 'error' or 'warning' those are literal values. + 要用於所報告事件的嚴重性。選項為: error (預設) warning。 + {Locked="error"}{Locked="warning"} diff --git a/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/RootFinder.cs b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/RootFinder.cs deleted file mode 100644 index aa4cf86d09..0000000000 --- a/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/RootFinder.cs +++ /dev/null @@ -1,34 +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.Extensions.AzureDevOpsReport; - -internal static class RootFinder -{ - private static string? s_root; - - public static string Find() - { - if (s_root != null) - { - return s_root; - } - - string path = AppContext.BaseDirectory; - string dir = path; - while (Directory.GetDirectoryRoot(dir) != dir) - { - if (Directory.Exists(Path.Combine(dir, ".git"))) - { - s_root = dir + Path.DirectorySeparatorChar; - return dir + Path.DirectorySeparatorChar; - } - else - { - dir = Directory.GetParent(dir)!.ToString(); - } - } - - throw new InvalidOperationException($"Could not find solution root, .git not found in {path} or any parent directory."); - } -} diff --git a/src/Platform/Microsoft.Testing.Extensions.CrashDump/BannedSymbols.txt b/src/Platform/Microsoft.Testing.Extensions.CrashDump/BannedSymbols.txt index ea8617fcb0..09d715c49c 100644 --- a/src/Platform/Microsoft.Testing.Extensions.CrashDump/BannedSymbols.txt +++ b/src/Platform/Microsoft.Testing.Extensions.CrashDump/BannedSymbols.txt @@ -6,5 +6,5 @@ M:System.Threading.Tasks.Task.WhenAll(System.Threading.Tasks.Task[]); Use 'ITask 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); 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/CrashDumpEnvironmentVariableProvider.cs b/src/Platform/Microsoft.Testing.Extensions.CrashDump/CrashDumpEnvironmentVariableProvider.cs index e23f9491ca..8ca9a523cb 100644 --- a/src/Platform/Microsoft.Testing.Extensions.CrashDump/CrashDumpEnvironmentVariableProvider.cs +++ b/src/Platform/Microsoft.Testing.Extensions.CrashDump/CrashDumpEnvironmentVariableProvider.cs @@ -21,7 +21,7 @@ internal sealed class CrashDumpEnvironmentVariableProvider : ITestHostEnvironmen private const string CreateDumpVerboseDiagnosticsVariable = "CreateDumpVerboseDiagnostics"; private const string EnableMiniDumpValue = "1"; - private readonly string[] _prefixes = ["DOTNET_", "COMPlus_"]; + private static readonly string[] Prefixes = ["DOTNET_", "COMPlus_"]; private readonly IConfiguration _configuration; private readonly ICommandLineOptions _commandLineOptions; private readonly ITestApplicationModuleInfo _testApplicationModuleInfo; @@ -62,7 +62,7 @@ public Task IsEnabledAsync() public Task UpdateAsync(IEnvironmentVariables environmentVariables) { - foreach (string prefix in _prefixes) + foreach (string prefix in Prefixes) { environmentVariables.SetVariable(new($"{prefix}{EnableMiniDumpVariable}", EnableMiniDumpValue, false, true)); environmentVariables.SetVariable(new($"{prefix}{CreateDumpDiagnosticsVariable}", EnableMiniDumpValue, false, true)); @@ -107,7 +107,7 @@ public Task UpdateAsync(IEnvironmentVariables environmentVariables) } } - foreach (string prefix in _prefixes) + foreach (string prefix in Prefixes) { environmentVariables.SetVariable(new($"{prefix}{MiniDumpTypeVariable}", miniDumpTypeValue, false, true)); } @@ -116,7 +116,7 @@ public Task UpdateAsync(IEnvironmentVariables environmentVariables) ? Path.Combine(_configuration.GetTestResultDirectory(), dumpFileName[0]) : Path.Combine(_configuration.GetTestResultDirectory(), $"{Path.GetFileNameWithoutExtension(_testApplicationModuleInfo.GetCurrentTestApplicationFullPath())}_%p_crash.dmp"); _crashDumpGeneratorConfiguration.DumpFileNamePattern = _miniDumpNameValue; - foreach (string prefix in _prefixes) + foreach (string prefix in Prefixes) { environmentVariables.SetVariable(new($"{prefix}{MiniDumpNameVariable}", _miniDumpNameValue, false, true)); } @@ -132,12 +132,11 @@ public Task UpdateAsync(IEnvironmentVariables environmentVariables) public Task ValidateTestHostEnvironmentVariablesAsync(IReadOnlyEnvironmentVariables environmentVariables) { - StringBuilder errors = new(); - #if !NETCOREAPP return ValidationResult.InvalidTask(CrashDumpResources.CrashDumpNotSupportedInNonNetCoreErrorMessage); #else - foreach (string prefix in _prefixes) + StringBuilder errors = new(); + foreach (string prefix in Prefixes) { if (!environmentVariables.TryGetVariable($"{prefix}{EnableMiniDumpVariable}", out OwnedEnvironmentVariable? enableMiniDump) || enableMiniDump.Value != EnableMiniDumpValue) @@ -146,7 +145,7 @@ public Task ValidateTestHostEnvironmentVariablesAsync(IReadOnl } } - foreach (string prefix in _prefixes) + foreach (string prefix in Prefixes) { if (!environmentVariables.TryGetVariable($"{prefix}{CreateDumpDiagnosticsVariable}", out OwnedEnvironmentVariable? enableMiniDump) || enableMiniDump.Value != EnableMiniDumpValue) @@ -155,7 +154,7 @@ public Task ValidateTestHostEnvironmentVariablesAsync(IReadOnl } } - foreach (string prefix in _prefixes) + foreach (string prefix in Prefixes) { if (!environmentVariables.TryGetVariable($"{prefix}{CreateDumpVerboseDiagnosticsVariable}", out OwnedEnvironmentVariable? enableMiniDump) || enableMiniDump.Value != EnableMiniDumpValue) @@ -164,7 +163,7 @@ public Task ValidateTestHostEnvironmentVariablesAsync(IReadOnl } } - foreach (string prefix in _prefixes) + foreach (string prefix in Prefixes) { if (!environmentVariables.TryGetVariable($"{prefix}{MiniDumpTypeVariable}", out OwnedEnvironmentVariable? miniDumpType)) { @@ -187,7 +186,7 @@ public Task ValidateTestHostEnvironmentVariablesAsync(IReadOnl } } - foreach (string prefix in _prefixes) + foreach (string prefix in Prefixes) { if (!environmentVariables.TryGetVariable($"{prefix}{MiniDumpNameVariable}", out OwnedEnvironmentVariable? miniDumpName) || miniDumpName.Value != _miniDumpNameValue) diff --git a/src/Platform/Microsoft.Testing.Extensions.CrashDump/CrashDumpProcessLifetimeHandler.cs b/src/Platform/Microsoft.Testing.Extensions.CrashDump/CrashDumpProcessLifetimeHandler.cs index ecec7a268e..005ccc1580 100644 --- a/src/Platform/Microsoft.Testing.Extensions.CrashDump/CrashDumpProcessLifetimeHandler.cs +++ b/src/Platform/Microsoft.Testing.Extensions.CrashDump/CrashDumpProcessLifetimeHandler.cs @@ -63,19 +63,19 @@ public async Task OnTestHostProcessExitedAsync(ITestHostProcessInformation testH } ApplicationStateGuard.Ensure(_netCoreCrashDumpGeneratorConfiguration.DumpFileNamePattern is not null); - await _outputDisplay.DisplayAsync(this, new ErrorMessageOutputDeviceData(string.Format(CultureInfo.InvariantCulture, CrashDumpResources.CrashDumpProcessCrashedDumpFileCreated, testHostProcessInformation.PID))); + await _outputDisplay.DisplayAsync(this, new ErrorMessageOutputDeviceData(string.Format(CultureInfo.InvariantCulture, CrashDumpResources.CrashDumpProcessCrashedDumpFileCreated, testHostProcessInformation.PID))).ConfigureAwait(false); 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)); + await _messageBus.PublishAsync(this, new FileArtifact(new FileInfo(expectedDumpFile), CrashDumpResources.CrashDumpArtifactDisplayName, CrashDumpResources.CrashDumpArtifactDescription)).ConfigureAwait(false); } else { - await _outputDisplay.DisplayAsync(this, new ErrorMessageOutputDeviceData(string.Format(CultureInfo.InvariantCulture, CrashDumpResources.CannotFindExpectedCrashDumpFile, expectedDumpFile))); + await _outputDisplay.DisplayAsync(this, new ErrorMessageOutputDeviceData(string.Format(CultureInfo.InvariantCulture, CrashDumpResources.CannotFindExpectedCrashDumpFile, expectedDumpFile))).ConfigureAwait(false); foreach (string dumpFile in Directory.GetFiles(Path.GetDirectoryName(expectedDumpFile)!, "*.dmp")) { - await _messageBus.PublishAsync(this, new FileArtifact(new FileInfo(dumpFile), CrashDumpResources.CrashDumpDisplayName, CrashDumpResources.CrashDumpArtifactDescription)); + await _messageBus.PublishAsync(this, new FileArtifact(new FileInfo(dumpFile), CrashDumpResources.CrashDumpDisplayName, CrashDumpResources.CrashDumpArtifactDescription)).ConfigureAwait(false); } } } 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 index 6d7d59466d..b6f17f7577 100644 --- a/src/Platform/Microsoft.Testing.Extensions.CrashDump/Microsoft.Testing.Extensions.CrashDump.csproj +++ b/src/Platform/Microsoft.Testing.Extensions.CrashDump/Microsoft.Testing.Extensions.CrashDump.csproj @@ -46,8 +46,4 @@ This package extends Microsoft Testing Platform to provide a crash dump function - - - - diff --git a/src/Platform/Microsoft.Testing.Extensions.HangDump/BannedSymbols.txt b/src/Platform/Microsoft.Testing.Extensions.HangDump/BannedSymbols.txt index ea8617fcb0..09d715c49c 100644 --- a/src/Platform/Microsoft.Testing.Extensions.HangDump/BannedSymbols.txt +++ b/src/Platform/Microsoft.Testing.Extensions.HangDump/BannedSymbols.txt @@ -6,5 +6,5 @@ M:System.Threading.Tasks.Task.WhenAll(System.Threading.Tasks.Task[]); Use 'ITask 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); 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 index 9469725e51..cde00c838d 100644 --- a/src/Platform/Microsoft.Testing.Extensions.HangDump/HangDumpActivityIndicator.cs +++ b/src/Platform/Microsoft.Testing.Extensions.HangDump/HangDumpActivityIndicator.cs @@ -65,7 +65,7 @@ public HangDumpActivityIndicator( 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 = new NamedPipeClient(namedPipeName, _environment); _namedPipeClient.RegisterSerializer(new ActivityIndicatorMutexNameRequestSerializer(), typeof(ActivityIndicatorMutexNameRequest)); _namedPipeClient.RegisterSerializer(new VoidResponseSerializer(), typeof(VoidResponse)); _namedPipeClient.RegisterSerializer(new SessionEndSerializerRequestSerializer(), typeof(SessionEndSerializerRequest)); @@ -90,7 +90,7 @@ public async Task OnTestSessionStartingAsync(SessionUid sessionUid, Cancellation { ApplicationStateGuard.Ensure(_namedPipeClient is not null); - if (!await IsEnabledAsync() || cancellationToken.IsCancellationRequested) + if (!await IsEnabledAsync().ConfigureAwait(false) || cancellationToken.IsCancellationRequested) { return; } @@ -98,36 +98,36 @@ public async Task OnTestSessionStartingAsync(SessionUid sessionUid, Cancellation 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, cancellationToken); - await _logger.LogTraceAsync("Connected to the process lifetime handler"); + await _logger.LogTraceAsync($"Connecting to the process lifetime handler {_namedPipeClient.PipeName}").ConfigureAwait(false); + await _namedPipeClient.ConnectAsync(cancellationToken).TimeoutAfterAsync(TimeoutHelper.DefaultHangTimeSpanTimeout, cancellationToken).ConfigureAwait(false); + await _logger.LogTraceAsync("Connected to the process lifetime handler").ConfigureAwait(false); _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"); + await _logger.LogTraceAsync($"Wait for mutex '{_mutexName}' creation").ConfigureAwait(false); _mutexCreated.Wait(cancellationToken); - await _logger.LogTraceAsync($"Mutex '{_mutexName}' created"); + await _logger.LogTraceAsync($"Mutex '{_mutexName}' created").ConfigureAwait(false); await _namedPipeClient.RequestReplyAsync(new ActivityIndicatorMutexNameRequest(_mutexName), cancellationToken) - .TimeoutAfterAsync(TimeoutHelper.DefaultHangTimeSpanTimeout, cancellationToken); - await _logger.LogTraceAsync($"Mutex '{_mutexName}' sent to the process lifetime handler"); + .TimeoutAfterAsync(TimeoutHelper.DefaultHangTimeSpanTimeout, cancellationToken).ConfigureAwait(false); + await _logger.LogTraceAsync($"Mutex '{_mutexName}' sent to the process lifetime handler").ConfigureAwait(false); // Setup the server channel with the testhost controller - _pipeNameDescription = NamedPipeServer.GetPipeName(Guid.NewGuid().ToString("N")); + _pipeNameDescription = NamedPipeServer.GetPipeName(Guid.NewGuid().ToString("N"), _environment); _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 _logger.LogTraceAsync($"Send consumer pipe name to the test controller '{_pipeNameDescription.Name}'").ConfigureAwait(false); await _namedPipeClient.RequestReplyAsync(new ConsumerPipeNameRequest(_pipeNameDescription.Name), cancellationToken) - .TimeoutAfterAsync(TimeoutHelper.DefaultHangTimeSpanTimeout, cancellationToken); + .TimeoutAfterAsync(TimeoutHelper.DefaultHangTimeSpanTimeout, cancellationToken).ConfigureAwait(false); // Wait the connection from the testhost controller - await _singleConnectionNamedPipeServer.WaitConnectionAsync(cancellationToken).TimeoutAfterAsync(TimeoutHelper.DefaultHangTimeSpanTimeout, cancellationToken); - await _logger.LogTraceAsync("Test host controller connected"); + await _singleConnectionNamedPipeServer.WaitConnectionAsync(cancellationToken).TimeoutAfterAsync(TimeoutHelper.DefaultHangTimeSpanTimeout, cancellationToken).ConfigureAwait(false); + await _logger.LogTraceAsync("Test host controller connected").ConfigureAwait(false); } catch (OperationCanceledException ex) when (ex.CancellationToken == cancellationToken) { @@ -139,13 +139,13 @@ private async Task CallbackAsync(IRequest request) { if (request is GetInProgressTestsRequest) { - await _logger.LogDebugAsync($"Received '{nameof(GetInProgressTestsRequest)}'"); - return new GetInProgressTestsResponse(_testsCurrentExecutionState.Select(x => (x.Value.Name, (int)_clock.UtcNow.Subtract(x.Value.StartTime).TotalSeconds)).ToArray()); + await _logger.LogDebugAsync($"Received '{nameof(GetInProgressTestsRequest)}'").ConfigureAwait(false); + return new GetInProgressTestsResponse([.. _testsCurrentExecutionState.Select(x => (x.Value.Name, (int)_clock.UtcNow.Subtract(x.Value.StartTime).TotalSeconds))]); } else if (request is ExitSignalActivityIndicatorTaskRequest) { - await _logger.LogDebugAsync($"Received '{nameof(ExitSignalActivityIndicatorTaskRequest)}'"); - await ExitSignalActivityIndicatorTaskAsync(); + await _logger.LogDebugAsync($"Received '{nameof(ExitSignalActivityIndicatorTaskRequest)}'").ConfigureAwait(false); + await ExitSignalActivityIndicatorTaskAsync().ConfigureAwait(false); return VoidResponse.CachedInstance; } else @@ -167,7 +167,7 @@ public async Task ConsumeAsync(IDataProducer dataProducer, IData value, Cancella { if (_traceLevelEnabled) { - await _logger.LogTraceAsync($"New in-progress test '{nodeChangedMessage.TestNode.DisplayName}'"); + await _logger.LogTraceAsync($"New in-progress test '{nodeChangedMessage.TestNode.DisplayName}'").ConfigureAwait(false); } _testsCurrentExecutionState.TryAdd(nodeChangedMessage.TestNode.Uid, (nodeChangedMessage.TestNode.DisplayName, typeof(InProgressTestNodeStateProperty), _clock.UtcNow)); @@ -177,7 +177,7 @@ or FailedTestNodeStateProperty or TimeoutTestNodeStateProperty or SkippedTestNod && _testsCurrentExecutionState.TryRemove(nodeChangedMessage.TestNode.Uid, out (string Name, Type Type, DateTimeOffset StartTime) record) && _traceLevelEnabled) { - await _logger.LogTraceAsync($"Test removed from in-progress list '{record.Name}' after '{_clock.UtcNow.Subtract(record.StartTime)}', total in-progress '{_testsCurrentExecutionState.Count}'"); + await _logger.LogTraceAsync($"Test removed from in-progress list '{record.Name}' after '{_clock.UtcNow.Subtract(record.StartTime)}', total in-progress '{_testsCurrentExecutionState.Count}'").ConfigureAwait(false); } // Optimization, we're interested in test progression and eventually in the discovery progression @@ -185,7 +185,7 @@ or FailedTestNodeStateProperty or TimeoutTestNodeStateProperty or SkippedTestNod { if (_traceLevelEnabled) { - await _logger.LogTraceAsync($"Signal for action node {nodeChangedMessage.TestNode.DisplayName} - '{state}'"); + await _logger.LogTraceAsync($"Signal for action node {nodeChangedMessage.TestNode.DisplayName} - '{state}'").ConfigureAwait(false); } // Signal the activity if it's not set @@ -234,18 +234,18 @@ public async Task OnTestSessionFinishingAsync(SessionUid sessionUid, Cancellatio ApplicationStateGuard.Ensure(_namedPipeClient is not null); ApplicationStateGuard.Ensure(_activityIndicatorMutex is not null); - if (!await IsEnabledAsync()) + if (!await IsEnabledAsync().ConfigureAwait(false)) { return; } await _namedPipeClient.RequestReplyAsync(new SessionEndSerializerRequest(), cancellationToken) - .TimeoutAfterAsync(TimeoutHelper.DefaultHangTimeSpanTimeout, cancellationToken); + .TimeoutAfterAsync(TimeoutHelper.DefaultHangTimeSpanTimeout, cancellationToken).ConfigureAwait(false); - await _logger.LogDebugAsync("Signal for test session end'"); - await ExitSignalActivityIndicatorTaskAsync(); + await _logger.LogDebugAsync("Signal for test session end'").ConfigureAwait(false); + await ExitSignalActivityIndicatorTaskAsync().ConfigureAwait(false); - await _logger.LogTraceAsync("Signaled by process for it's exit"); + await _logger.LogTraceAsync("Signaled by process for it's exit").ConfigureAwait(false); _sessionEndCalled = true; } @@ -259,20 +259,20 @@ private async Task ExitSignalActivityIndicatorTaskAsync() ApplicationStateGuard.Ensure(_signalActivityIndicatorTask is not null); _exitSignalActivityIndicatorAsync = true; _signalActivity.Set(); - await _signalActivityIndicatorTask.TimeoutAfterAsync(TimeoutHelper.DefaultHangTimeSpanTimeout); + await _signalActivityIndicatorTask.TimeoutAfterAsync(TimeoutHelper.DefaultHangTimeSpanTimeout).ConfigureAwait(false); } #if NETCOREAPP public async ValueTask DisposeAsync() { - await DisposeHelper.DisposeAsync(_namedPipeClient); + await DisposeHelper.DisposeAsync(_namedPipeClient).ConfigureAwait(false); // 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); + await DisposeHelper.DisposeAsync(_singleConnectionNamedPipeServer).ConfigureAwait(false); } _pipeNameDescription?.Dispose(); diff --git a/src/Platform/Microsoft.Testing.Extensions.HangDump/HangDumpExtensions.cs b/src/Platform/Microsoft.Testing.Extensions.HangDump/HangDumpExtensions.cs index 1ecc644a4e..e0ce018e0b 100644 --- a/src/Platform/Microsoft.Testing.Extensions.HangDump/HangDumpExtensions.cs +++ b/src/Platform/Microsoft.Testing.Extensions.HangDump/HangDumpExtensions.cs @@ -21,9 +21,10 @@ public static class HangDumpExtensions /// The test application builder. public static void AddHangDumpProvider(this ITestApplicationBuilder builder) { - CurrentTestApplicationModuleInfo testApplicationModuleInfo = new(new SystemEnvironment(), new SystemProcessHandler()); + var environment = new SystemEnvironment(); + CurrentTestApplicationModuleInfo testApplicationModuleInfo = new(environment, new SystemProcessHandler()); string mutexSuffix = Guid.NewGuid().ToString("N"); - PipeNameDescription pipeNameDescription = NamedPipeServer.GetPipeName(Guid.NewGuid().ToString("N")); + PipeNameDescription pipeNameDescription = NamedPipeServer.GetPipeName(Guid.NewGuid().ToString("N"), environment); HangDumpConfiguration hangDumpConfiguration = new(testApplicationModuleInfo, pipeNameDescription, mutexSuffix); builder.TestHostControllers.AddProcessLifetimeHandler(serviceProvider @@ -35,7 +36,6 @@ public static void AddHangDumpProvider(this ITestApplicationBuilder builder) serviceProvider.GetTask(), serviceProvider.GetEnvironment(), serviceProvider.GetLoggerFactory(), - serviceProvider.GetTestApplicationModuleInfo(), serviceProvider.GetConfiguration(), serviceProvider.GetProcessHandler(), serviceProvider, diff --git a/src/Platform/Microsoft.Testing.Extensions.HangDump/HangDumpProcessLifetimeHandler.cs b/src/Platform/Microsoft.Testing.Extensions.HangDump/HangDumpProcessLifetimeHandler.cs index 06f1fa1044..79f8ce2c02 100644 --- a/src/Platform/Microsoft.Testing.Extensions.HangDump/HangDumpProcessLifetimeHandler.cs +++ b/src/Platform/Microsoft.Testing.Extensions.HangDump/HangDumpProcessLifetimeHandler.cs @@ -1,9 +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.Extensions.Diagnostics.Helpers; 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; @@ -32,7 +32,7 @@ internal sealed class HangDumpProcessLifetimeHandler : ITestHostProcessLifetimeH #endif { private readonly IMessageBus _messageBus; - private readonly IOutputDevice _outputDisplay; + private readonly OutputDeviceWriter _outputDisplay; private readonly ICommandLineOptions _commandLineOptions; private readonly ITask _task; private readonly IEnvironment _environment; @@ -45,6 +45,7 @@ internal sealed class HangDumpProcessLifetimeHandler : ITestHostProcessLifetimeH private readonly ILogger _logger; private readonly ManualResetEventSlim _mutexNameReceived = new(false); private readonly ManualResetEventSlim _waitConsumerPipeName = new(false); + private readonly List _dumpFiles = []; private TimeSpan _activityTimerValue = TimeSpan.FromMinutes(30); private Task? _waitConnectionTask; @@ -53,21 +54,19 @@ internal sealed class HangDumpProcessLifetimeHandler : ITestHostProcessLifetimeH private string? _activityTimerMutexName; private bool _exitActivityIndicatorTask; private string _dumpType = "Full"; - private string _dumpFileNamePattern; + private string? _dumpFileNamePattern; private Mutex? _activityIndicatorMutex; private ITestHostProcessInformation? _testHostProcessInformation; - private string _dumpFileTaken = string.Empty; private NamedPipeClient? _namedPipeClient; public HangDumpProcessLifetimeHandler( PipeNameDescription pipeNameDescription, IMessageBus messageBus, - IOutputDevice outputDisplay, + IOutputDevice outputDevice, ICommandLineOptions commandLineOptions, ITask task, IEnvironment environment, ILoggerFactory loggerFactory, - ITestApplicationModuleInfo testApplicationModuleInfo, IConfiguration configuration, IProcessHandler processHandler, IServiceProvider serviceProvider, @@ -77,7 +76,7 @@ public HangDumpProcessLifetimeHandler( _traceEnabled = _logger.IsEnabled(LogLevel.Trace); _pipeNameDescription = pipeNameDescription; _messageBus = messageBus; - _outputDisplay = outputDisplay; + _outputDisplay = new OutputDeviceWriter(outputDevice, this); _commandLineOptions = commandLineOptions; _task = task; _environment = environment; @@ -85,7 +84,6 @@ public HangDumpProcessLifetimeHandler( _processHandler = processHandler; _clock = clock; _testApplicationCancellationTokenSource = serviceProvider.GetTestApplicationCancellationTokenSource(); - _dumpFileNamePattern = $"{Path.GetFileNameWithoutExtension(testApplicationModuleInfo.GetCurrentTestApplicationFullPath())}_%p_hang.dmp"; } public string Uid => nameof(HangDumpProcessLifetimeHandler); @@ -118,7 +116,7 @@ public async Task BeforeTestHostProcessStartAsync(CancellationToken cancellation _dumpFileNamePattern = fileName[0]; } - await _logger.LogInformationAsync($"Hang dump timeout setup {_activityTimerValue}."); + await _logger.LogInformationAsync($"Hang dump timeout setup {_activityTimerValue}.").ConfigureAwait(false); _waitConnectionTask = _task.Run( async () => @@ -128,8 +126,8 @@ public async Task BeforeTestHostProcessStartAsync(CancellationToken cancellation _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); + await _logger.LogDebugAsync($"Waiting for connection to {_singleConnectionNamedPipeServer.PipeName.Name}").ConfigureAwait(false); + await _singleConnectionNamedPipeServer.WaitConnectionAsync(cancellationToken).TimeoutAfterAsync(TimeoutHelper.DefaultHangTimeSpanTimeout, cancellationToken).ConfigureAwait(false); }, cancellationToken); } @@ -137,19 +135,19 @@ private async Task CallbackAsync(IRequest request) { if (request is ActivityIndicatorMutexNameRequest activityIndicatorMutexNameRequest) { - await _logger.LogDebugAsync($"Mutex name received by the test host, '{activityIndicatorMutexNameRequest.MutexName}'"); + await _logger.LogDebugAsync($"Mutex name received by the test host, '{activityIndicatorMutexNameRequest.MutexName}'").ConfigureAwait(false); _activityTimerMutexName = activityIndicatorMutexNameRequest.MutexName; _mutexNameReceived.Set(); return VoidResponse.CachedInstance; } else if (request is SessionEndSerializerRequest) { - await _logger.LogDebugAsync("Session end received by the test host"); + await _logger.LogDebugAsync("Session end received by the test host").ConfigureAwait(false); _exitActivityIndicatorTask = true; #if NET if (_namedPipeClient is not null) { - await _namedPipeClient.DisposeAsync(); + await _namedPipeClient.DisposeAsync().ConfigureAwait(false); } #else _namedPipeClient?.Dispose(); @@ -158,8 +156,8 @@ private async Task CallbackAsync(IRequest request) } else if (request is ConsumerPipeNameRequest consumerPipeNameRequest) { - await _logger.LogDebugAsync($"Consumer pipe name received '{consumerPipeNameRequest.PipeName}'"); - _namedPipeClient = new NamedPipeClient(consumerPipeNameRequest.PipeName); + await _logger.LogDebugAsync($"Consumer pipe name received '{consumerPipeNameRequest.PipeName}'").ConfigureAwait(false); + _namedPipeClient = new NamedPipeClient(consumerPipeNameRequest.PipeName, _environment); _namedPipeClient.RegisterSerializer(new GetInProgressTestsResponseSerializer(), typeof(GetInProgressTestsResponse)); _namedPipeClient.RegisterSerializer(new GetInProgressTestsRequestSerializer(), typeof(GetInProgressTestsRequest)); _namedPipeClient.RegisterSerializer(new ExitSignalActivityIndicatorTaskRequestSerializer(), typeof(ExitSignalActivityIndicatorTaskRequest)); @@ -181,14 +179,14 @@ public async Task OnTestHostProcessStartedAsync(ITestHostProcessInformation test { _testHostProcessInformation = testHostProcessInformation; - await _logger.LogDebugAsync($"Wait for test host connection to the server pipe '{_singleConnectionNamedPipeServer.PipeName.Name}'"); - await _waitConnectionTask.TimeoutAfterAsync(TimeoutHelper.DefaultHangTimeSpanTimeout); + await _logger.LogDebugAsync($"Wait for test host connection to the server pipe '{_singleConnectionNamedPipeServer.PipeName.Name}'").ConfigureAwait(false); + await _waitConnectionTask.TimeoutAfterAsync(TimeoutHelper.DefaultHangTimeSpanTimeout).ConfigureAwait(false); 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}'"); + await _namedPipeClient.ConnectAsync(cancellation).TimeoutAfterAsync(TimeoutHelper.DefaultHangTimeSpanTimeout).ConfigureAwait(false); + await _logger.LogDebugAsync($"Connected to the test host server pipe '{_namedPipeClient.PipeName}'").ConfigureAwait(false); // Keep the custom thread to avoid to waste one from thread pool. _activityIndicatorTask = _task.RunLongRunning( @@ -196,11 +194,11 @@ public async Task OnTestHostProcessStartedAsync(ITestHostProcessInformation test { try { - await ActivityTimerAsync(); + await ActivityTimerAsync(cancellation).ConfigureAwait(false); } catch (Exception e) { - await _outputDisplay.DisplayAsync(this, new ErrorMessageOutputDeviceData(string.Format(CultureInfo.InvariantCulture, ExtensionResources.HangDumpFailed, e.ToString(), GetDiskInfo()))); + await _outputDisplay.DisplayAsync(new ErrorMessageOutputDeviceData(string.Format(CultureInfo.InvariantCulture, ExtensionResources.HangDumpFailed, e.ToString(), GetDiskInfo()))).ConfigureAwait(false); throw; } }, "[HangDump] ActivityTimerAsync", cancellation); @@ -242,17 +240,17 @@ public async Task OnTestHostProcessExitedAsync(ITestHostProcessInformation testH _activityIndicatorMutex?.Dispose(); } - if (!RoslynString.IsNullOrEmpty(_dumpFileTaken)) + foreach (string dumpFile in _dumpFiles) { - await _messageBus.PublishAsync(this, new FileArtifact(new FileInfo(_dumpFileTaken), ExtensionResources.HangDumpArtifactDisplayName, ExtensionResources.HangDumpArtifactDescription)); + await _messageBus.PublishAsync(this, new FileArtifact(new FileInfo(dumpFile), ExtensionResources.HangDumpArtifactDisplayName, ExtensionResources.HangDumpArtifactDescription)).ConfigureAwait(false); } } - private async Task ActivityTimerAsync() + private async Task ActivityTimerAsync(CancellationToken cancellationToken) { _logger.LogDebug("Wait for mutex name from the test host"); - if (!_mutexNameReceived.Wait(TimeoutHelper.DefaultHangTimeSpanTimeout)) + if (!_mutexNameReceived.Wait(TimeoutHelper.DefaultHangTimeSpanTimeout, cancellationToken)) { throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, ExtensionResources.MutexNameReceptionTimeoutErrorMessage, TimeoutHelper.DefaultHangTimeoutSeconds)); } @@ -340,87 +338,172 @@ private async Task ActivityTimerAsync() if (timeoutFired) { - await TakeDumpAsync(); + await TakeDumpOfTreeAsync(cancellationToken).ConfigureAwait(false); } } - private async Task TakeDumpAsync() + private async Task TakeDumpOfTreeAsync(CancellationToken cancellationToken) { ApplicationStateGuard.Ensure(_testHostProcessInformation is not null); - ApplicationStateGuard.Ensure(_dumpType is not null); - await _logger.LogInformationAsync($"Hang dump timeout({_activityTimerValue}) expired."); - await _outputDisplay.DisplayAsync(this, new ErrorMessageOutputDeviceData(string.Format(CultureInfo.InvariantCulture, ExtensionResources.HangDumpTimeoutExpired, _activityTimerValue))); + await _logger.LogInformationAsync($"Hang dump timeout({_activityTimerValue}) expired.").ConfigureAwait(false); + await _outputDisplay.DisplayAsync(new ErrorMessageOutputDeviceData(string.Format(CultureInfo.InvariantCulture, ExtensionResources.HangDumpTimeoutExpired, _activityTimerValue))).ConfigureAwait(false); + + using IProcess process = _processHandler.GetProcessById(_testHostProcessInformation.PID); + var processTree = (await process.GetProcessTreeAsync(_logger, _outputDisplay, cancellationToken).ConfigureAwait(false)).Where(p => p.Process?.Name is not null and not "conhost" and not "WerFault").ToList(); + IEnumerable bottomUpTree = processTree.OrderByDescending(t => t.Level).Select(t => t.Process).OfType(); + + try + { + if (processTree.Count > 1) + { + await _outputDisplay.DisplayAsync(new ErrorMessageOutputDeviceData(ExtensionResources.DumpingProcessTree)).ConfigureAwait(false); + + foreach (ProcessTreeNode? p in processTree.OrderBy(t => t.Level)) + { + await _outputDisplay.DisplayAsync(new ErrorMessageOutputDeviceData($"{(p.Level != 0 ? " + " : " > ")}{new string('-', p.Level)} {p.Process!.Id} - {p.Process.Name}")).ConfigureAwait(false); + } + } + else + { + await _outputDisplay.DisplayAsync(new ErrorMessageOutputDeviceData(string.Format(CultureInfo.InvariantCulture, ExtensionResources.DumpingProcess, process.Id, process.Name))).ConfigureAwait(false); + } + + await _logger.LogInformationAsync($"Hang dump timeout({_activityTimerValue}) expired.").ConfigureAwait(false); + + // Do not suspend processes with NetClient dumper it stops the diagnostic thread running in + // them and hang dump request will get stuck forever, because the process is not co-operating. + // Instead we start one task per dump asynchronously, and hope that the parent process will start dumping + // before the child process is done dumping. This way if the parent is waiting for the children to exit, + // we will be dumping it before it observes the child exiting and we get a more accurate results. If we did not + // do this, then parent that is awaiting child might exit before we get to dumping it. + foreach (IProcess p in bottomUpTree) + { + try + { + await TakeDumpAsync(p).ConfigureAwait(false); + } + catch (Exception e) + { + // exceptions.Add(new InvalidOperationException($"Error while taking dump of process {p.Name} {p.Id}", e)); + await _logger.LogErrorAsync($"Error while taking dump of process {p.Id} - {p.Name}", e).ConfigureAwait(false); + await _outputDisplay.DisplayAsync(new ErrorMessageOutputDeviceData(string.Format(CultureInfo.InvariantCulture, ExtensionResources.ErrorWhileDumpingProcess, p.Id, p.Name, e))).ConfigureAwait(false); + } + } + } + finally + { + NotifyCrashDumpServiceIfEnabled(); + + // Some of the processes might crashed, which breaks the process tree (on windows it is just an illusion), + // so try extra hard to kill all the known processes in the tree, since we already spent a bunch of time getting + // to know which processes are involved. + foreach (ProcessTreeNode node in processTree) + { + IProcess? p = node.Process; + if (p == null) + { + continue; + } - string finalDumpFileName = _dumpFileNamePattern.Replace("%p", _testHostProcessInformation.PID.ToString(CultureInfo.InvariantCulture)); + try + { + if (!p.HasExited) + { + p.Kill(); + await p.WaitForExitAsync().ConfigureAwait(false); + } + } + catch (Exception e) + { + await _logger.LogErrorAsync($"Problem killing {p.Id} - {p.Name}", e).ConfigureAwait(false); + await _outputDisplay.DisplayAsync(new ErrorMessageOutputDeviceData(string.Format(CultureInfo.InvariantCulture, ExtensionResources.ErrorKillingProcess, p.Id, p.Name, e))).ConfigureAwait(false); + } + } + } + } + + private async Task TakeDumpAsync(IProcess process) + { + ApplicationStateGuard.Ensure(_testHostProcessInformation is not null); + ApplicationStateGuard.Ensure(_dumpType is not null); + + string finalDumpFileName = (_dumpFileNamePattern ?? $"{process.Name}_%p_hang.dmp").Replace("%p", process.Id.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); + GetInProgressTestsResponse tests = await _namedPipeClient.RequestReplyAsync(new GetInProgressTestsRequest(), _testApplicationCancellationTokenSource.CancellationToken).ConfigureAwait(false); + await _namedPipeClient.RequestReplyAsync(new ExitSignalActivityIndicatorTaskRequest(), _testApplicationCancellationTokenSource.CancellationToken).ConfigureAwait(false); 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, new ErrorMessageOutputDeviceData(ExtensionResources.RunningTestsWhileDumping)); + await _outputDisplay.DisplayAsync(new ErrorMessageOutputDeviceData(ExtensionResources.RunningTestsWhileDumping)).ConfigureAwait(false); foreach ((string testName, int seconds) in tests.Tests) { - await sw.WriteLineAsync($"[{TimeSpan.FromSeconds(seconds)}] {testName}"); - await _outputDisplay.DisplayAsync(this, new ErrorMessageOutputDeviceData($"[{TimeSpan.FromSeconds(seconds)}] {testName}")); + await sw.WriteLineAsync($"[{TimeSpan.FromSeconds(seconds)}] {testName}").ConfigureAwait(false); + await _outputDisplay.DisplayAsync(new ErrorMessageOutputDeviceData($"[{TimeSpan.FromSeconds(seconds)}] {testName}")).ConfigureAwait(false); } } - await _messageBus.PublishAsync(this, new FileArtifact(new FileInfo(hangTestsFileName), ExtensionResources.HangTestListArtifactDisplayName, ExtensionResources.HangTestListArtifactDescription)); + await _messageBus.PublishAsync(this, new FileArtifact(new FileInfo(hangTestsFileName), ExtensionResources.HangTestListArtifactDisplayName, ExtensionResources.HangTestListArtifactDescription)).ConfigureAwait(false); } - await _logger.LogInformationAsync($"Creating dump filename {finalDumpFileName}"); + await _logger.LogInformationAsync($"Creating dump filename {finalDumpFileName}").ConfigureAwait(false); + + await _outputDisplay.DisplayAsync(new ErrorMessageOutputDeviceData(string.Format(CultureInfo.InvariantCulture, ExtensionResources.CreatingDumpFile, finalDumpFileName))).ConfigureAwait(false); - await _outputDisplay.DisplayAsync(this, new ErrorMessageOutputDeviceData(string.Format(CultureInfo.InvariantCulture, ExtensionResources.CreatingDumpFile, finalDumpFileName))); +#if NETCOREAPP + DiagnosticsClient diagnosticsClient = new(process.Id); + DumpType dumpType = _dumpType.ToLowerInvariant().Trim() switch + { + "mini" => DumpType.Normal, + "heap" => DumpType.WithHeap, + "triage" => DumpType.Triage, + "full" => DumpType.Full, + _ => throw ApplicationStateGuard.Unreachable(), + }; + + // Wrap the dump path into "" when it has space in it, this is a workaround for this runtime issue: https://github.com/dotnet/diagnostics/issues/5020 + // It only affects windows. Otherwise the dump creation fails with: [createdump] The pid argument is no longer supported + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows) && finalDumpFileName.Contains(' ')) + { + finalDumpFileName = $"\"{finalDumpFileName}\""; + } try { -#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(), - }; - - // Wrap the dump path into "" when it has space in it, this is a workaround for this runtime issue: https://github.com/dotnet/diagnostics/issues/5020 - // It only affects windows. Otherwise the dump creation fails with: [createdump] The pid argument is no longer supported - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows) && finalDumpFileName.Contains(' ')) - { - finalDumpFileName = $"\"{finalDumpFileName}\""; - } + diagnosticsClient.WriteDump(dumpType, finalDumpFileName, logDumpGeneration: false); + } + catch (Exception e) + { + await _logger.LogErrorAsync($"Error while writing dump of process {process.Name} {process.Id}", e).ConfigureAwait(false); + await _outputDisplay.DisplayAsync(new ErrorMessageOutputDeviceData(string.Format(CultureInfo.InvariantCulture, ExtensionResources.ErrorWhileDumpingProcess, process.Id, process.Name, e))).ConfigureAwait(false); + } - diagnosticsClient.WriteDump(dumpType, finalDumpFileName, true); #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); -#endif + MiniDumpWriteDump.MiniDumpTypeOption miniDumpTypeOption = _dumpType.ToLowerInvariant().Trim() switch + { + "mini" => MiniDumpWriteDump.MiniDumpTypeOption.Mini, + "heap" => MiniDumpWriteDump.MiniDumpTypeOption.Heap, + "full" => MiniDumpWriteDump.MiniDumpTypeOption.Full, + _ => throw ApplicationStateGuard.Unreachable(), + }; - _dumpFileTaken = finalDumpFileName; + try + { + MiniDumpWriteDump.CollectDumpUsingMiniDumpWriteDump(process.Id, finalDumpFileName, miniDumpTypeOption); } - finally + catch (Exception e) { - NotifyCrashDumpServiceIfEnabled(); - using IProcess process = _processHandler.GetProcessById(_testHostProcessInformation.PID); - process.Kill(); - await process.WaitForExitAsync(); + await _logger.LogErrorAsync($"Error while writing dump of process {process.Name} {process.Id}", e).ConfigureAwait(false); + await _outputDisplay.DisplayAsync(new ErrorMessageOutputDeviceData(string.Format(CultureInfo.InvariantCulture, ExtensionResources.ErrorWhileDumpingProcess, process.Id, process.Name, e))).ConfigureAwait(false); } +#endif + + _dumpFiles.Add(finalDumpFileName); } private static void NotifyCrashDumpServiceIfEnabled() @@ -448,7 +531,7 @@ public async ValueTask DisposeAsync() { if (_activityIndicatorTask is not null) { - await _activityIndicatorTask.TimeoutAfterAsync(TimeoutHelper.DefaultHangTimeSpanTimeout); + await _activityIndicatorTask.TimeoutAfterAsync(TimeoutHelper.DefaultHangTimeSpanTimeout).ConfigureAwait(false); } _namedPipeClient?.Dispose(); @@ -459,3 +542,23 @@ public async ValueTask DisposeAsync() } #endif } + +internal sealed class OutputDeviceWriter +{ + private readonly IOutputDevice _outputDevice; + private readonly IOutputDeviceDataProducer _outputDeviceDataProducer; + + public OutputDeviceWriter(IOutputDevice outputDevice, IOutputDeviceDataProducer outputDeviceDataProducer) + { + _outputDevice = outputDevice; + _outputDeviceDataProducer = outputDeviceDataProducer; + } + + /// + /// Displays the output data asynchronously, using the stored producer. + /// + /// The output data. + /// A task representing the asynchronous operation. + public async Task DisplayAsync(IOutputDeviceData data) + => await _outputDevice.DisplayAsync(_outputDeviceDataProducer, data).ConfigureAwait(false); +} diff --git a/src/Platform/Microsoft.Testing.Extensions.HangDump/Helpers/IProcessExtensions.cs b/src/Platform/Microsoft.Testing.Extensions.HangDump/Helpers/IProcessExtensions.cs new file mode 100644 index 0000000000..5ff92df9e8 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.HangDump/Helpers/IProcessExtensions.cs @@ -0,0 +1,188 @@ +// 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.Resources; +using Microsoft.Testing.Platform; +using Microsoft.Testing.Platform.Helpers; +using Microsoft.Testing.Platform.Logging; +using Microsoft.Testing.Platform.OutputDevice; + +namespace Microsoft.Testing.Extensions.Diagnostics.Helpers; + +/// +/// Helper functions for IProcess. +/// +internal static class IProcessExtensions +{ + private const int InvalidProcessId = -1; + + public static async Task> GetProcessTreeAsync(this IProcess process, ILogger logger, OutputDeviceWriter outputDisplay, CancellationToken cancellationToken) + { + var childProcesses = Process.GetProcesses() + .Where(p => IsChildCandidate(p, process)) + .ToList(); + + var acc = new List(); + foreach (Process c in childProcesses) + { + cancellationToken.ThrowIfCancellationRequested(); + + try + { + int parentId = await GetParentPidAsync(c, logger, outputDisplay).ConfigureAwait(false); + + acc.Add(new ProcessTreeNode { ParentId = parentId, Process = new SystemProcess(c) }); + } + catch (Exception e) + { + logger.LogError($"Failed to get parent for process {c.Id} - {c.ProcessName}", e); + await outputDisplay.DisplayAsync(new ErrorMessageOutputDeviceData(string.Format(CultureInfo.InvariantCulture, ExtensionResources.ErrorGettingParentOfProcess, c.Id, c.ProcessName, e))).ConfigureAwait(false); + } + } + + int level = 1; + int limit = 10; + ResolveChildren(process, logger, acc, level, limit); + + return [new() { Process = process, Level = 0 }, .. acc.Where(a => a.Level > 0)]; + } + + /// + /// Returns the parent id of a process or -1 if it fails. + /// + /// The process to find parent of. + /// The logger. + /// The output display.git. + /// The pid of the parent process. + internal static async Task GetParentPidAsync(Process process, ILogger logger, OutputDeviceWriter outputDisplay) + => RuntimeInformation.IsOSPlatform(OSPlatform.Windows) + ? await GetParentPidWindowsAsync(process).ConfigureAwait(false) + : RuntimeInformation.IsOSPlatform(OSPlatform.Linux) + ? await GetParentPidLinuxAsync(process).ConfigureAwait(false) + : RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? + await GetParentPidMacOsAsync(process, logger, outputDisplay).ConfigureAwait(false) + : throw new PlatformNotSupportedException(); + + internal static Task GetParentPidWindowsAsync(Process process) + { + IntPtr handle = process.Handle; + int res = NtQueryInformationProcess(handle, 0, out ProcessBasicInformation pbi, Marshal.SizeOf(), out int _); + + int p = res != 0 ? InvalidProcessId : pbi.InheritedFromUniqueProcessId.ToInt32(); + + return Task.FromResult(p); + } + + /// Read the /proc file system for information about the parent. + /// The process to get the parent process from. + /// The process id. + internal static Task GetParentPidLinuxAsync(Process process) + { + int pid = process.Id; + + // read /proc//stat + // 4th column will contain the ppid, 92 in the example below + // ex: 93 (bash) S 92 93 2 4294967295 ... + string path = $"/proc/{pid}/stat"; + string stat = File.ReadAllText(path); + string[] parts = stat.Split(' '); + + int ppid = parts.Length < 5 ? InvalidProcessId : int.Parse(parts[3], CultureInfo.CurrentCulture); + + return Task.FromResult(ppid); + } + + internal static async Task GetParentPidMacOsAsync(Process process, ILogger logger, OutputDeviceWriter outputDisplay) + { + var output = new StringBuilder(); + var err = new StringBuilder(); + Process ps = new(); + ps.StartInfo.FileName = "ps"; + ps.StartInfo.Arguments = $"-o ppid= {process.Id}"; + ps.StartInfo.UseShellExecute = false; + ps.StartInfo.RedirectStandardOutput = true; + ps.StartInfo.RedirectStandardError = true; + ps.OutputDataReceived += (_, e) => output.Append(e.Data); + ps.ErrorDataReceived += (_, e) => err.Append(e.Data); + ps.Start(); + ps.BeginOutputReadLine(); + ps.BeginErrorReadLine(); + + int timeout = 5_000; +#if !NETSTANDARD2_0 + // This will read the output streams till the end. + await ps.WaitForExitAsync(new CancellationTokenSource(timeout).Token).ConfigureAwait(false); +#else + // This won't read the streams till the end. If we timeout, we need to use the + // wait without timeout, but that can hang, so we offload that into a task so we can abandon it and continue. + if (!ps.WaitForExit(timeout)) + { + await Task.Run(ps.WaitForExit, new CancellationTokenSource(timeout).Token).ConfigureAwait(false); + } +#endif + + string o = output.ToString(); + string e = err.ToString(); + + int parent = int.TryParse(o.Trim(), out int ppid) ? ppid : InvalidProcessId; + + if (!RoslynString.IsNullOrWhiteSpace(e)) + { + logger.LogError($"Error getting parent of process {process.Id} - {process.ProcessName}, {e}."); + await outputDisplay.DisplayAsync(new ErrorMessageOutputDeviceData(string.Format(CultureInfo.InvariantCulture, ExtensionResources.ErrorGettingParentOfProcess, process.Id, process.ProcessName, e))).ConfigureAwait(false); + } + + return parent; + } + + private static void ResolveChildren(IProcess parent, ILogger logger, List acc, int level, int limit) + { + if (limit < 0) + { + // hit recursion limit, just returning + return; + } + + // only take children that are newer than the parent, because process ids (PIDs) get recycled + var children = acc.Where(p => p.ParentId == parent.Id && p.Process?.StartTime > parent.StartTime).ToList(); + + foreach (ProcessTreeNode child in children) + { + child.Level = level; + ResolveChildren(child.Process!, logger, acc, level + 1, limit); + } + } + + private static bool IsChildCandidate(Process child, IProcess parent) + { + // this is extremely slow under debugger, but fast without it + try + { + return child.StartTime > parent.StartTime && child.Id != parent.Id; + } + catch + { + /* access denied or process has exits most likely */ + return false; + } + } + + [StructLayout(LayoutKind.Sequential)] + private struct ProcessBasicInformation + { + public readonly IntPtr ExitStatus; + public readonly IntPtr PebBaseAddress; + public readonly IntPtr AffinityMask; + public readonly IntPtr BasePriority; + public readonly IntPtr UniqueProcessId; + public IntPtr InheritedFromUniqueProcessId; + } + + [DllImport("ntdll.dll", SetLastError = true)] + private static extern int NtQueryInformationProcess( + IntPtr processHandle, + int processInformationClass, + out ProcessBasicInformation processInformation, + int processInformationLength, + out int returnLength); +} diff --git a/src/Platform/Microsoft.Testing.Extensions.HangDump/Helpers/ProcessTreeNode.cs b/src/Platform/Microsoft.Testing.Extensions.HangDump/Helpers/ProcessTreeNode.cs new file mode 100644 index 0000000000..0b8d1aa99a --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.HangDump/Helpers/ProcessTreeNode.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. + +using Microsoft.Testing.Platform.Helpers; + +namespace Microsoft.Testing.Extensions.Diagnostics.Helpers; + +internal sealed class ProcessTreeNode +{ + public IProcess? Process { get; set; } + + public int Level { get; set; } + + public int ParentId { get; set; } + + public Process? ParentProcess { get; set; } +} 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 index d115d99e12..a7766d483a 100644 --- a/src/Platform/Microsoft.Testing.Extensions.HangDump/Microsoft.Testing.Extensions.HangDump.csproj +++ b/src/Platform/Microsoft.Testing.Extensions.HangDump/Microsoft.Testing.Extensions.HangDump.csproj @@ -43,10 +43,6 @@ This package extends Microsoft Testing Platform to provide an implementation of - - - - diff --git a/src/Platform/Microsoft.Testing.Extensions.HangDump/Resources/ExtensionResources.resx b/src/Platform/Microsoft.Testing.Extensions.HangDump/Resources/ExtensionResources.resx index c49f7c7c4d..9f26cb952d 100644 --- a/src/Platform/Microsoft.Testing.Extensions.HangDump/Resources/ExtensionResources.resx +++ b/src/Platform/Microsoft.Testing.Extensions.HangDump/Resources/ExtensionResources.resx @@ -120,6 +120,25 @@ Creating dump file '{0}' + + Dumping process {0} - {1} + 0 is pid, 1 is the name of the process + + + Dumping this process tree (from bottom): + + + Error getting parent of process {0} - {1}: {2}" + 0 is pid, 1 is name, 2 is the exception + + + Error while killing process {0} - {1}: {2} + 0 is pid, 1 is name, 2 is the exception + + + Error while taking dump of process {0} - {1}: {2} + 0 is pid, 1 is name, 2 is the exception from the system + The hang dump 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 index ca975dba15..84139c78be 100644 --- a/src/Platform/Microsoft.Testing.Extensions.HangDump/Resources/xlf/ExtensionResources.cs.xlf +++ b/src/Platform/Microsoft.Testing.Extensions.HangDump/Resources/xlf/ExtensionResources.cs.xlf @@ -7,6 +7,31 @@ Vytváří se soubor výpisu paměti {0}. + + Dumping process {0} - {1} + Dumping process {0} - {1} + 0 is pid, 1 is the name of the process + + + Dumping this process tree (from bottom): + Dumping this process tree (from bottom): + + + + Error getting parent of process {0} - {1}: {2}" + Error getting parent of process {0} - {1}: {2}" + 0 is pid, 1 is name, 2 is the exception + + + Error while killing process {0} - {1}: {2} + Error while killing process {0} - {1}: {2} + 0 is pid, 1 is name, 2 is the exception + + + Error while taking dump of process {0} - {1}: {2} + Error while taking dump of process {0} - {1}: {2} + 0 is pid, 1 is name, 2 is the exception from the system + The hang dump file Soubor výpisu paměti při zablokování 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 index c2984eecb4..dfd150b085 100644 --- a/src/Platform/Microsoft.Testing.Extensions.HangDump/Resources/xlf/ExtensionResources.de.xlf +++ b/src/Platform/Microsoft.Testing.Extensions.HangDump/Resources/xlf/ExtensionResources.de.xlf @@ -7,6 +7,31 @@ Speicherabbilddatei "{0}" wird erstellt + + Dumping process {0} - {1} + Dumping process {0} - {1} + 0 is pid, 1 is the name of the process + + + Dumping this process tree (from bottom): + Dumping this process tree (from bottom): + + + + Error getting parent of process {0} - {1}: {2}" + Error getting parent of process {0} - {1}: {2}" + 0 is pid, 1 is name, 2 is the exception + + + Error while killing process {0} - {1}: {2} + Error while killing process {0} - {1}: {2} + 0 is pid, 1 is name, 2 is the exception + + + Error while taking dump of process {0} - {1}: {2} + Error while taking dump of process {0} - {1}: {2} + 0 is pid, 1 is name, 2 is the exception from the system + The hang dump file Die Absturzspeicherabbilddatei 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 index bd2919495c..82228ab65e 100644 --- a/src/Platform/Microsoft.Testing.Extensions.HangDump/Resources/xlf/ExtensionResources.es.xlf +++ b/src/Platform/Microsoft.Testing.Extensions.HangDump/Resources/xlf/ExtensionResources.es.xlf @@ -7,6 +7,31 @@ Creando archivo de volcado '{0}' + + Dumping process {0} - {1} + Dumping process {0} - {1} + 0 is pid, 1 is the name of the process + + + Dumping this process tree (from bottom): + Dumping this process tree (from bottom): + + + + Error getting parent of process {0} - {1}: {2}" + Error getting parent of process {0} - {1}: {2}" + 0 is pid, 1 is name, 2 is the exception + + + Error while killing process {0} - {1}: {2} + Error while killing process {0} - {1}: {2} + 0 is pid, 1 is name, 2 is the exception + + + Error while taking dump of process {0} - {1}: {2} + Error while taking dump of process {0} - {1}: {2} + 0 is pid, 1 is name, 2 is the exception from the system + The hang dump file El archivo de volcado de memoria 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 index 62e6150c3e..f4fae09fd4 100644 --- a/src/Platform/Microsoft.Testing.Extensions.HangDump/Resources/xlf/ExtensionResources.fr.xlf +++ b/src/Platform/Microsoft.Testing.Extensions.HangDump/Resources/xlf/ExtensionResources.fr.xlf @@ -7,6 +7,31 @@ Création du fichier de vidage «{0}» + + Dumping process {0} - {1} + Dumping process {0} - {1} + 0 is pid, 1 is the name of the process + + + Dumping this process tree (from bottom): + Dumping this process tree (from bottom): + + + + Error getting parent of process {0} - {1}: {2}" + Error getting parent of process {0} - {1}: {2}" + 0 is pid, 1 is name, 2 is the exception + + + Error while killing process {0} - {1}: {2} + Error while killing process {0} - {1}: {2} + 0 is pid, 1 is name, 2 is the exception + + + Error while taking dump of process {0} - {1}: {2} + Error while taking dump of process {0} - {1}: {2} + 0 is pid, 1 is name, 2 is the exception from the system + The hang dump file Fichier de vidage de blocage 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 index 753a327bc3..a639e8bb5a 100644 --- a/src/Platform/Microsoft.Testing.Extensions.HangDump/Resources/xlf/ExtensionResources.it.xlf +++ b/src/Platform/Microsoft.Testing.Extensions.HangDump/Resources/xlf/ExtensionResources.it.xlf @@ -7,6 +7,31 @@ Creazione del file di dump '{0}' + + Dumping process {0} - {1} + Dumping process {0} - {1} + 0 is pid, 1 is the name of the process + + + Dumping this process tree (from bottom): + Dumping this process tree (from bottom): + + + + Error getting parent of process {0} - {1}: {2}" + Error getting parent of process {0} - {1}: {2}" + 0 is pid, 1 is name, 2 is the exception + + + Error while killing process {0} - {1}: {2} + Error while killing process {0} - {1}: {2} + 0 is pid, 1 is name, 2 is the exception + + + Error while taking dump of process {0} - {1}: {2} + Error while taking dump of process {0} - {1}: {2} + 0 is pid, 1 is name, 2 is the exception from the system + The hang dump file Il file di dump di blocco 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 index bd9a93f75a..3de6abfab9 100644 --- a/src/Platform/Microsoft.Testing.Extensions.HangDump/Resources/xlf/ExtensionResources.ja.xlf +++ b/src/Platform/Microsoft.Testing.Extensions.HangDump/Resources/xlf/ExtensionResources.ja.xlf @@ -7,6 +7,31 @@ ダンプ ファイル '{0}' を作成しています + + Dumping process {0} - {1} + Dumping process {0} - {1} + 0 is pid, 1 is the name of the process + + + Dumping this process tree (from bottom): + Dumping this process tree (from bottom): + + + + Error getting parent of process {0} - {1}: {2}" + Error getting parent of process {0} - {1}: {2}" + 0 is pid, 1 is name, 2 is the exception + + + Error while killing process {0} - {1}: {2} + Error while killing process {0} - {1}: {2} + 0 is pid, 1 is name, 2 is the exception + + + Error while taking dump of process {0} - {1}: {2} + Error while taking dump of process {0} - {1}: {2} + 0 is pid, 1 is name, 2 is the exception from the system + The hang dump 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 index 310583594e..70a9ee5648 100644 --- a/src/Platform/Microsoft.Testing.Extensions.HangDump/Resources/xlf/ExtensionResources.ko.xlf +++ b/src/Platform/Microsoft.Testing.Extensions.HangDump/Resources/xlf/ExtensionResources.ko.xlf @@ -7,6 +7,31 @@ 덤프 파일 '{0}'을(를) 만드는 중 + + Dumping process {0} - {1} + Dumping process {0} - {1} + 0 is pid, 1 is the name of the process + + + Dumping this process tree (from bottom): + Dumping this process tree (from bottom): + + + + Error getting parent of process {0} - {1}: {2}" + Error getting parent of process {0} - {1}: {2}" + 0 is pid, 1 is name, 2 is the exception + + + Error while killing process {0} - {1}: {2} + Error while killing process {0} - {1}: {2} + 0 is pid, 1 is name, 2 is the exception + + + Error while taking dump of process {0} - {1}: {2} + Error while taking dump of process {0} - {1}: {2} + 0 is pid, 1 is name, 2 is the exception from the system + The hang dump 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 index e3a316ebf1..c205dce991 100644 --- a/src/Platform/Microsoft.Testing.Extensions.HangDump/Resources/xlf/ExtensionResources.pl.xlf +++ b/src/Platform/Microsoft.Testing.Extensions.HangDump/Resources/xlf/ExtensionResources.pl.xlf @@ -7,6 +7,31 @@ Tworzenie pliku zrzutu „{0}” + + Dumping process {0} - {1} + Dumping process {0} - {1} + 0 is pid, 1 is the name of the process + + + Dumping this process tree (from bottom): + Dumping this process tree (from bottom): + + + + Error getting parent of process {0} - {1}: {2}" + Error getting parent of process {0} - {1}: {2}" + 0 is pid, 1 is name, 2 is the exception + + + Error while killing process {0} - {1}: {2} + Error while killing process {0} - {1}: {2} + 0 is pid, 1 is name, 2 is the exception + + + Error while taking dump of process {0} - {1}: {2} + Error while taking dump of process {0} - {1}: {2} + 0 is pid, 1 is name, 2 is the exception from the system + The hang dump file Plik zrzutu zawieszenia 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 index 437cca313e..585619f8f4 100644 --- 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 @@ -7,6 +7,31 @@ Criando arquivo de despejo ''{0}'' + + Dumping process {0} - {1} + Dumping process {0} - {1} + 0 is pid, 1 is the name of the process + + + Dumping this process tree (from bottom): + Dumping this process tree (from bottom): + + + + Error getting parent of process {0} - {1}: {2}" + Error getting parent of process {0} - {1}: {2}" + 0 is pid, 1 is name, 2 is the exception + + + Error while killing process {0} - {1}: {2} + Error while killing process {0} - {1}: {2} + 0 is pid, 1 is name, 2 is the exception + + + Error while taking dump of process {0} - {1}: {2} + Error while taking dump of process {0} - {1}: {2} + 0 is pid, 1 is name, 2 is the exception from the system + The hang dump file O arquivo de despejo de travamento 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 index d0e463df9d..79810063a9 100644 --- a/src/Platform/Microsoft.Testing.Extensions.HangDump/Resources/xlf/ExtensionResources.ru.xlf +++ b/src/Platform/Microsoft.Testing.Extensions.HangDump/Resources/xlf/ExtensionResources.ru.xlf @@ -7,6 +7,31 @@ Создание файла дампа "{0}" + + Dumping process {0} - {1} + Dumping process {0} - {1} + 0 is pid, 1 is the name of the process + + + Dumping this process tree (from bottom): + Dumping this process tree (from bottom): + + + + Error getting parent of process {0} - {1}: {2}" + Error getting parent of process {0} - {1}: {2}" + 0 is pid, 1 is name, 2 is the exception + + + Error while killing process {0} - {1}: {2} + Error while killing process {0} - {1}: {2} + 0 is pid, 1 is name, 2 is the exception + + + Error while taking dump of process {0} - {1}: {2} + Error while taking dump of process {0} - {1}: {2} + 0 is pid, 1 is name, 2 is the exception from the system + The hang dump 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 index 9dd85248e8..c0b9eaa844 100644 --- a/src/Platform/Microsoft.Testing.Extensions.HangDump/Resources/xlf/ExtensionResources.tr.xlf +++ b/src/Platform/Microsoft.Testing.Extensions.HangDump/Resources/xlf/ExtensionResources.tr.xlf @@ -7,6 +7,31 @@ Döküm dosyası '{0}' oluşturuluyor + + Dumping process {0} - {1} + Dumping process {0} - {1} + 0 is pid, 1 is the name of the process + + + Dumping this process tree (from bottom): + Dumping this process tree (from bottom): + + + + Error getting parent of process {0} - {1}: {2}" + Error getting parent of process {0} - {1}: {2}" + 0 is pid, 1 is name, 2 is the exception + + + Error while killing process {0} - {1}: {2} + Error while killing process {0} - {1}: {2} + 0 is pid, 1 is name, 2 is the exception + + + Error while taking dump of process {0} - {1}: {2} + Error while taking dump of process {0} - {1}: {2} + 0 is pid, 1 is name, 2 is the exception from the system + The hang dump file Askıda kalma dökümü dosyası 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 index ef45b929ac..f1d1efbd76 100644 --- 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 @@ -7,6 +7,31 @@ 正在创建转储文件“{0}” + + Dumping process {0} - {1} + Dumping process {0} - {1} + 0 is pid, 1 is the name of the process + + + Dumping this process tree (from bottom): + Dumping this process tree (from bottom): + + + + Error getting parent of process {0} - {1}: {2}" + Error getting parent of process {0} - {1}: {2}" + 0 is pid, 1 is name, 2 is the exception + + + Error while killing process {0} - {1}: {2} + Error while killing process {0} - {1}: {2} + 0 is pid, 1 is name, 2 is the exception + + + Error while taking dump of process {0} - {1}: {2} + Error while taking dump of process {0} - {1}: {2} + 0 is pid, 1 is name, 2 is the exception from the system + The hang dump 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 index 157985b3e7..811a36819b 100644 --- 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 @@ -7,6 +7,31 @@ 正在建立傾印檔案 '{0}' + + Dumping process {0} - {1} + Dumping process {0} - {1} + 0 is pid, 1 is the name of the process + + + Dumping this process tree (from bottom): + Dumping this process tree (from bottom): + + + + Error getting parent of process {0} - {1}: {2}" + Error getting parent of process {0} - {1}: {2}" + 0 is pid, 1 is name, 2 is the exception + + + Error while killing process {0} - {1}: {2} + Error while killing process {0} - {1}: {2} + 0 is pid, 1 is name, 2 is the exception + + + Error while taking dump of process {0} - {1}: {2} + Error while taking dump of process {0} - {1}: {2} + 0 is pid, 1 is name, 2 is the exception from the system + The hang dump file 擱置傾印檔案 diff --git a/src/Platform/Microsoft.Testing.Extensions.HangDump/Serializers/ExitSignalActivityIndicatorTaskRequest.cs b/src/Platform/Microsoft.Testing.Extensions.HangDump/Serializers/ExitSignalActivityIndicatorTaskRequest.cs index a1f76cf3b9..72ab5a9bd0 100644 --- a/src/Platform/Microsoft.Testing.Extensions.HangDump/Serializers/ExitSignalActivityIndicatorTaskRequest.cs +++ b/src/Platform/Microsoft.Testing.Extensions.HangDump/Serializers/ExitSignalActivityIndicatorTaskRequest.cs @@ -6,7 +6,7 @@ namespace Microsoft.Testing.Extensions.HangDump.Serializers; -internal sealed class ExitSignalActivityIndicatorTaskRequest() : IRequest; +internal sealed class ExitSignalActivityIndicatorTaskRequest : IRequest; internal sealed class ExitSignalActivityIndicatorTaskRequestSerializer : BaseSerializer, INamedPipeSerializer { diff --git a/src/Platform/Microsoft.Testing.Extensions.HangDump/Serializers/GetInProgressTestsRequest.cs b/src/Platform/Microsoft.Testing.Extensions.HangDump/Serializers/GetInProgressTestsRequest.cs index 2a68a98905..05cbf08b45 100644 --- a/src/Platform/Microsoft.Testing.Extensions.HangDump/Serializers/GetInProgressTestsRequest.cs +++ b/src/Platform/Microsoft.Testing.Extensions.HangDump/Serializers/GetInProgressTestsRequest.cs @@ -6,7 +6,7 @@ namespace Microsoft.Testing.Extensions.HangDump.Serializers; -internal sealed class GetInProgressTestsRequest() : IRequest; +internal sealed class GetInProgressTestsRequest : IRequest; internal sealed class GetInProgressTestsRequestSerializer : BaseSerializer, INamedPipeSerializer { diff --git a/src/Platform/Microsoft.Testing.Extensions.HangDump/Serializers/GetInProgressTestsResponse.cs b/src/Platform/Microsoft.Testing.Extensions.HangDump/Serializers/GetInProgressTestsResponse.cs index da92375d71..04344f941c 100644 --- a/src/Platform/Microsoft.Testing.Extensions.HangDump/Serializers/GetInProgressTestsResponse.cs +++ b/src/Platform/Microsoft.Testing.Extensions.HangDump/Serializers/GetInProgressTestsResponse.cs @@ -26,7 +26,7 @@ public object Deserialize(Stream stream) tests.Add((testName, unixTimeSeconds)); } - return new GetInProgressTestsResponse(tests.ToArray()); + return new GetInProgressTestsResponse([.. tests]); } public void Serialize(object objectToSerialize, Stream stream) diff --git a/src/Platform/Microsoft.Testing.Extensions.HangDump/Serializers/SessionEndSerializer.cs b/src/Platform/Microsoft.Testing.Extensions.HangDump/Serializers/SessionEndSerializer.cs index 23cd132c19..8e547f6974 100644 --- a/src/Platform/Microsoft.Testing.Extensions.HangDump/Serializers/SessionEndSerializer.cs +++ b/src/Platform/Microsoft.Testing.Extensions.HangDump/Serializers/SessionEndSerializer.cs @@ -6,7 +6,7 @@ namespace Microsoft.Testing.Extensions.HangDump.Serializers; -internal sealed class SessionEndSerializerRequest() : IRequest; +internal sealed class SessionEndSerializerRequest : IRequest; internal sealed class SessionEndSerializerRequestSerializer : BaseSerializer, INamedPipeSerializer { diff --git a/src/Platform/Microsoft.Testing.Extensions.HangDump/WindowsMiniDumpWriteDump.cs b/src/Platform/Microsoft.Testing.Extensions.HangDump/WindowsMiniDumpWriteDump.cs index 41f2c6586d..d069d6e9f1 100644 --- a/src/Platform/Microsoft.Testing.Extensions.HangDump/WindowsMiniDumpWriteDump.cs +++ b/src/Platform/Microsoft.Testing.Extensions.HangDump/WindowsMiniDumpWriteDump.cs @@ -9,7 +9,7 @@ internal static class MiniDumpWriteDump { public static void CollectDumpUsingMiniDumpWriteDump(int pid, string outputFile, MiniDumpTypeOption type) { - var process = Process.GetProcessById(pid); + using var process = Process.GetProcessById(pid); // Open the file for writing using var stream = new FileStream(outputFile, FileMode.Create, FileAccess.ReadWrite, FileShare.None); diff --git a/src/Platform/Microsoft.Testing.Extensions.HotReload/BannedSymbols.txt b/src/Platform/Microsoft.Testing.Extensions.HotReload/BannedSymbols.txt index 5bc5c2fb96..6b0c437d6e 100644 --- a/src/Platform/Microsoft.Testing.Extensions.HotReload/BannedSymbols.txt +++ b/src/Platform/Microsoft.Testing.Extensions.HotReload/BannedSymbols.txt @@ -6,5 +6,5 @@ M:System.Threading.Tasks.Task.WhenAll(System.Threading.Tasks.Task[]); Use 'ITask 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); 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.HotReload/HotReloadHandler.cs b/src/Platform/Microsoft.Testing.Extensions.HotReload/HotReloadHandler.cs index 281048572a..812d925074 100644 --- a/src/Platform/Microsoft.Testing.Extensions.HotReload/HotReloadHandler.cs +++ b/src/Platform/Microsoft.Testing.Extensions.HotReload/HotReloadHandler.cs @@ -84,13 +84,13 @@ public async Task ShouldRunAsync(Task? waitExecutionCompletion, Cancellati if (waitExecutionCompletion is not null) { - await waitExecutionCompletion; - await _outputDevice!.DisplayAsync(_outputDeviceDataProducer, new TextOutputDeviceData(ExtensionResources.HotReloadSessionCompleted)); + await waitExecutionCompletion.ConfigureAwait(false); + await _outputDevice!.DisplayAsync(_outputDeviceDataProducer, new TextOutputDeviceData(ExtensionResources.HotReloadSessionCompleted)).ConfigureAwait(false); } try { - await SemaphoreSlim.WaitAsync(cancellationToken); + await SemaphoreSlim.WaitAsync(cancellationToken).ConfigureAwait(false); } catch (OperationCanceledException) when (cancellationToken.IsCancellationRequested) { @@ -102,7 +102,7 @@ public async Task ShouldRunAsync(Task? waitExecutionCompletion, Cancellati _console!.Clear(); } - await _outputDevice.DisplayAsync(_outputDeviceDataProducer, new TextOutputDeviceData(ExtensionResources.HotReloadSessionStarted)); + await _outputDevice.DisplayAsync(_outputDeviceDataProducer, new TextOutputDeviceData(ExtensionResources.HotReloadSessionStarted)).ConfigureAwait(false); return !s_shutdownProcess; } diff --git a/src/Platform/Microsoft.Testing.Extensions.HotReload/HotReloadTestHostTestFrameworkInvoker.cs b/src/Platform/Microsoft.Testing.Extensions.HotReload/HotReloadTestHostTestFrameworkInvoker.cs index df6c08a9d4..f28b457c82 100644 --- a/src/Platform/Microsoft.Testing.Extensions.HotReload/HotReloadTestHostTestFrameworkInvoker.cs +++ b/src/Platform/Microsoft.Testing.Extensions.HotReload/HotReloadTestHostTestFrameworkInvoker.cs @@ -33,7 +33,7 @@ public override async Task ExecuteRequestAsync(ITestFramework testFrameworkAdapt { if (!_isHotReloadEnabled) { - await base.ExecuteRequestAsync(testFrameworkAdapter, request, messageBus, cancellationToken); + await base.ExecuteRequestAsync(testFrameworkAdapter, request, messageBus, cancellationToken).ConfigureAwait(false); return; } @@ -41,25 +41,23 @@ public override async Task ExecuteRequestAsync(ITestFramework testFrameworkAdapt IOutputDevice outputDevice = ServiceProvider.GetOutputDevice(); var hotReloadHandler = new HotReloadHandler(ServiceProvider.GetConsole(), outputDevice, this); TaskCompletionSource? executionCompleted = null; - while (await hotReloadHandler.ShouldRunAsync(executionCompleted?.Task, ServiceProvider.GetTestApplicationCancellationTokenSource().CancellationToken)) + while (await hotReloadHandler.ShouldRunAsync(executionCompleted?.Task, ServiceProvider.GetTestApplicationCancellationTokenSource().CancellationToken).ConfigureAwait(false)) { executionCompleted = new(); using SemaphoreSlim requestSemaphore = new(1); var hotReloadOutputDevice = ServiceProvider.GetPlatformOutputDevice() as IHotReloadPlatformOutputDevice; if (hotReloadOutputDevice is not null) { - await hotReloadOutputDevice.DisplayBeforeHotReloadSessionStartAsync(); + await hotReloadOutputDevice.DisplayBeforeHotReloadSessionStartAsync().ConfigureAwait(false); } -#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. - await testFrameworkAdapter.ExecuteRequestAsync(new(request, messageBus, new SemaphoreSlimRequestCompleteNotifier(requestSemaphore), cancellationToken)); -#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. + await testFrameworkAdapter.ExecuteRequestAsync(new(request, messageBus, new SemaphoreSlimRequestCompleteNotifier(requestSemaphore), cancellationToken)).ConfigureAwait(false); - await requestSemaphore.WaitAsync(cancellationToken); - await ServiceProvider.GetBaseMessageBus().DrainDataAsync(); + await requestSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false); + await ServiceProvider.GetBaseMessageBus().DrainDataAsync().ConfigureAwait(false); if (hotReloadOutputDevice is not null) { - await hotReloadOutputDevice.DisplayAfterHotReloadSessionEndAsync(); + await hotReloadOutputDevice.DisplayAfterHotReloadSessionEndAsync().ConfigureAwait(false); } executionCompleted.SetResult(0); diff --git a/src/Platform/Microsoft.Testing.Extensions.HotReload/Microsoft.Testing.Extensions.HotReload.csproj b/src/Platform/Microsoft.Testing.Extensions.HotReload/Microsoft.Testing.Extensions.HotReload.csproj index a7e8e9b6f7..91dad273e7 100644 --- a/src/Platform/Microsoft.Testing.Extensions.HotReload/Microsoft.Testing.Extensions.HotReload.csproj +++ b/src/Platform/Microsoft.Testing.Extensions.HotReload/Microsoft.Testing.Extensions.HotReload.csproj @@ -43,19 +43,4 @@ This package extends Microsoft Testing Platform to provide Hot Reload support.]] - - - True - True - ExtensionResources.resx - - - - - - ResXFileCodeGenerator - ExtensionResources.Designer.cs - - - \ No newline at end of file diff --git a/src/Platform/Microsoft.Testing.Extensions.HotReload/Resources/ExtensionResources.Designer.cs b/src/Platform/Microsoft.Testing.Extensions.HotReload/Resources/ExtensionResources.Designer.cs deleted file mode 100644 index 4e6bc289c6..0000000000 --- a/src/Platform/Microsoft.Testing.Extensions.HotReload/Resources/ExtensionResources.Designer.cs +++ /dev/null @@ -1,90 +0,0 @@ -//------------------------------------------------------------------------------ -// -// 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.Hosting.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.Hosting.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 Hot reload is only supported on .NET 6.0 or greater. - /// - internal static string HotReloadHandlerUnsupportedFrameworkErrorMessage { - get { - return ResourceManager.GetString("HotReloadHandlerUnsupportedFrameworkErrorMessage", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Hot reload test session completed. - /// - internal static string HotReloadSessionCompleted { - get { - return ResourceManager.GetString("HotReloadSessionCompleted", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Hot reload test session started. - /// - internal static string HotReloadSessionStarted { - get { - return ResourceManager.GetString("HotReloadSessionStarted", resourceCulture); - } - } - } -} diff --git a/src/Platform/Microsoft.Testing.Extensions.MSBuild/MSBuildConsumer.cs b/src/Platform/Microsoft.Testing.Extensions.MSBuild/MSBuildConsumer.cs index 124be7cbb5..9ce87925d1 100644 --- a/src/Platform/Microsoft.Testing.Extensions.MSBuild/MSBuildConsumer.cs +++ b/src/Platform/Microsoft.Testing.Extensions.MSBuild/MSBuildConsumer.cs @@ -94,7 +94,7 @@ await HandleFailuresAsync( actual: null, testFileLocationProperty?.FilePath, testFileLocationProperty?.LineSpan.Start.Line ?? 0, - cancellationToken); + cancellationToken).ConfigureAwait(false); break; case FailedTestNodeStateProperty failedState: @@ -108,7 +108,7 @@ await HandleFailuresAsync( actual: failedState.Exception?.Data["assert.actual"] as string, testFileLocationProperty?.FilePath, testFileLocationProperty?.LineSpan.Start.Line ?? 0, - cancellationToken); + cancellationToken).ConfigureAwait(false); break; case TimeoutTestNodeStateProperty timeoutState: @@ -122,7 +122,7 @@ await HandleFailuresAsync( actual: null, testFileLocationProperty?.FilePath, testFileLocationProperty?.LineSpan.Start.Line ?? 0, - cancellationToken); + cancellationToken).ConfigureAwait(false); break; case CancelledTestNodeStateProperty canceledState: @@ -136,7 +136,7 @@ await HandleFailuresAsync( actual: null, testFileLocationProperty?.FilePath, testFileLocationProperty?.LineSpan.Start.Line ?? 0, - cancellationToken); + cancellationToken).ConfigureAwait(false); break; case PassedTestNodeStateProperty: @@ -153,7 +153,7 @@ await HandleFailuresAsync( break; case TestRequestExecutionTimeInfo testRequestExecutionTimeInfo: - await HandleSummaryAsync(testRequestExecutionTimeInfo, cancellationToken); + await HandleSummaryAsync(testRequestExecutionTimeInfo, cancellationToken).ConfigureAwait(false); break; } @@ -166,7 +166,7 @@ private async Task HandleFailuresAsync(string testDisplayName, bool isCanceled, ApplicationStateGuard.Ensure(_msBuildTestApplicationLifecycleCallbacks != null); ApplicationStateGuard.Ensure(_msBuildTestApplicationLifecycleCallbacks.PipeClient != null); var failedTestInfoRequest = new FailedTestInfoRequest(testDisplayName, isCanceled, duration, errorMessage, errorStackTrace, expected, actual, codeFilePath, lineNumber); - await _msBuildTestApplicationLifecycleCallbacks.PipeClient.RequestReplyAsync(failedTestInfoRequest, cancellationToken); + await _msBuildTestApplicationLifecycleCallbacks.PipeClient.RequestReplyAsync(failedTestInfoRequest, cancellationToken).ConfigureAwait(false); } private async Task HandleSummaryAsync(TestRequestExecutionTimeInfo timeInfo, CancellationToken cancellationToken) @@ -176,7 +176,7 @@ private async Task HandleSummaryAsync(TestRequestExecutionTimeInfo timeInfo, Can ApplicationStateGuard.Ensure(_msBuildTestApplicationLifecycleCallbacks != null); ApplicationStateGuard.Ensure(_msBuildTestApplicationLifecycleCallbacks.PipeClient != null); var runSummaryInfoRequest = new RunSummaryInfoRequest(_totalTests, _totalFailedTests, _totalPassedTests, _totalSkippedTests, duration); - await _msBuildTestApplicationLifecycleCallbacks.PipeClient.RequestReplyAsync(runSummaryInfoRequest, cancellationToken); + await _msBuildTestApplicationLifecycleCallbacks.PipeClient.RequestReplyAsync(runSummaryInfoRequest, cancellationToken).ConfigureAwait(false); } private static string? ToHumanReadableDuration(double? durationInMs) diff --git a/src/Platform/Microsoft.Testing.Extensions.MSBuild/MSBuildExtensions.cs b/src/Platform/Microsoft.Testing.Extensions.MSBuild/MSBuildExtensions.cs index cbd3d784be..5f9509761c 100644 --- a/src/Platform/Microsoft.Testing.Extensions.MSBuild/MSBuildExtensions.cs +++ b/src/Platform/Microsoft.Testing.Extensions.MSBuild/MSBuildExtensions.cs @@ -20,12 +20,18 @@ public static class MSBuildExtensions public static void AddMSBuild(this ITestApplicationBuilder builder) { builder.CommandLine.AddProvider(() => new MSBuildCommandLineProvider()); - builder.TestHost.AddTestApplicationLifecycleCallbacks( + builder.TestHost.AddTestHostApplicationLifetime( serviceProvider => new MSBuildTestApplicationLifecycleCallbacks( serviceProvider.GetConfiguration(), serviceProvider.GetCommandLineOptions(), serviceProvider.GetTestApplicationCancellationTokenSource())); + ((TestApplicationBuilder)builder).TestHostOrchestrator.AddTestHostOrchestratorApplicationLifetime( + serviceProvider => new MSBuildOrchestratorLifetime( + serviceProvider.GetConfiguration(), + serviceProvider.GetCommandLineOptions(), + serviceProvider.GetTestApplicationCancellationTokenSource())); + CompositeExtensionFactory compositeExtensionFactory = new(serviceProvider => new MSBuildConsumer( serviceProvider, diff --git a/src/Platform/Microsoft.Testing.Extensions.MSBuild/MSBuildOrchestratorLifecycleCallbacks.cs b/src/Platform/Microsoft.Testing.Extensions.MSBuild/MSBuildOrchestratorLifecycleCallbacks.cs new file mode 100644 index 0000000000..b74ba02f2f --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.MSBuild/MSBuildOrchestratorLifecycleCallbacks.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. + +using Microsoft.Testing.Extensions.MSBuild.Serializers; +using Microsoft.Testing.Platform.CommandLine; +using Microsoft.Testing.Platform.Configurations; +using Microsoft.Testing.Platform.Extensions.TestHostOrchestrator; +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.Services; + +namespace Microsoft.Testing.Extensions.MSBuild; + +internal sealed class MSBuildOrchestratorLifetime : ITestHostOrchestratorApplicationLifetime +{ + private readonly IConfiguration _configuration; + private readonly ICommandLineOptions _commandLineOptions; + private readonly ITestApplicationCancellationTokenSource _testApplicationCancellationTokenSource; + + public MSBuildOrchestratorLifetime( + IConfiguration configuration, + ICommandLineOptions commandLineOptions, + ITestApplicationCancellationTokenSource testApplicationCancellationTokenSource) + { + _configuration = configuration; + _commandLineOptions = commandLineOptions; + _testApplicationCancellationTokenSource = testApplicationCancellationTokenSource; + } + + public string Uid => nameof(MSBuildOrchestratorLifetime); + + public string Version => AppVersion.DefaultSemVer; + + public string DisplayName => nameof(MSBuildOrchestratorLifetime); + + public string Description => Resources.ExtensionResources.MSBuildExtensionsDescription; + + public Task IsEnabledAsync() + => Task.FromResult(_commandLineOptions.IsOptionSet(MSBuildConstants.MSBuildNodeOptionKey)); + + public async Task BeforeRunAsync(CancellationToken cancellationToken) + { + if (!_commandLineOptions.TryGetOptionArgumentList(MSBuildConstants.MSBuildNodeOptionKey, out string[]? msbuildInfo)) + { + throw new InvalidOperationException($"MSBuild pipe name not found in the command line, missing {MSBuildConstants.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 {MSBuildConstants.MSBuildNodeOptionKey}"); + } + + using var pipeClient = new NamedPipeClient(msbuildInfo[0]); + pipeClient.RegisterSerializer(new ModuleInfoRequestSerializer(), typeof(ModuleInfoRequest)); + pipeClient.RegisterSerializer(new VoidResponseSerializer(), typeof(VoidResponse)); + using var cancellationTokenSource = new CancellationTokenSource(TimeoutHelper.DefaultHangTimeSpanTimeout); + using var linkedCancellationToken = CancellationTokenSource.CreateLinkedTokenSource(cancellationTokenSource.Token, _testApplicationCancellationTokenSource.CancellationToken); + await pipeClient.ConnectAsync(linkedCancellationToken.Token).ConfigureAwait(false); + await pipeClient.RequestReplyAsync( + new ModuleInfoRequest( + RuntimeInformation.FrameworkDescription, + RuntimeInformation.ProcessArchitecture.ToString().ToLowerInvariant(), + _configuration.GetTestResultDirectory()), + _testApplicationCancellationTokenSource.CancellationToken).ConfigureAwait(false); + } + + public Task AfterRunAsync(int exitCode, CancellationToken cancellation) => Task.CompletedTask; +} diff --git a/src/Platform/Microsoft.Testing.Extensions.MSBuild/MSBuildTestApplicationLifecycleCallbacks.cs b/src/Platform/Microsoft.Testing.Extensions.MSBuild/MSBuildTestApplicationLifecycleCallbacks.cs index 7e830ab2b7..b8b91ecb4f 100644 --- a/src/Platform/Microsoft.Testing.Extensions.MSBuild/MSBuildTestApplicationLifecycleCallbacks.cs +++ b/src/Platform/Microsoft.Testing.Extensions.MSBuild/MSBuildTestApplicationLifecycleCallbacks.cs @@ -13,7 +13,7 @@ namespace Microsoft.Testing.Extensions.MSBuild; -internal sealed class MSBuildTestApplicationLifecycleCallbacks : ITestApplicationLifecycleCallbacks, IDisposable +internal sealed class MSBuildTestApplicationLifecycleCallbacks : ITestHostApplicationLifetime, IDisposable { private readonly IConfiguration _configuration; private readonly ICommandLineOptions _commandLineOptions; @@ -61,13 +61,13 @@ public async Task BeforeRunAsync(CancellationToken cancellationToken) 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.ConnectAsync(linkedCancellationToken.Token).ConfigureAwait(false); await PipeClient.RequestReplyAsync( new ModuleInfoRequest( RuntimeInformation.FrameworkDescription, RuntimeInformation.ProcessArchitecture.ToString().ToLowerInvariant(), _configuration.GetTestResultDirectory()), - _testApplicationCancellationTokenSource.CancellationToken); + _testApplicationCancellationTokenSource.CancellationToken).ConfigureAwait(false); } public void Dispose() diff --git a/src/Platform/Microsoft.Testing.Extensions.MSBuild/Microsoft.Testing.Extensions.MSBuild.csproj b/src/Platform/Microsoft.Testing.Extensions.MSBuild/Microsoft.Testing.Extensions.MSBuild.csproj index ba0721a0bd..33a50cbf53 100644 --- a/src/Platform/Microsoft.Testing.Extensions.MSBuild/Microsoft.Testing.Extensions.MSBuild.csproj +++ b/src/Platform/Microsoft.Testing.Extensions.MSBuild/Microsoft.Testing.Extensions.MSBuild.csproj @@ -7,15 +7,16 @@ - + + - + - + diff --git a/src/Platform/Microsoft.Testing.Extensions.MSBuild/PublicAPI.Shipped.txt b/src/Platform/Microsoft.Testing.Extensions.MSBuild/PublicAPI.Shipped.txt new file mode 100644 index 0000000000..a82cec2234 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.MSBuild/PublicAPI.Shipped.txt @@ -0,0 +1,5 @@ +#nullable enable +Microsoft.Testing.Platform.MSBuild.MSBuildExtensions +Microsoft.Testing.Platform.MSBuild.TestingPlatformBuilderHook +static Microsoft.Testing.Platform.MSBuild.MSBuildExtensions.AddMSBuild(this Microsoft.Testing.Platform.Builder.ITestApplicationBuilder! builder) -> void +static Microsoft.Testing.Platform.MSBuild.TestingPlatformBuilderHook.AddExtensions(Microsoft.Testing.Platform.Builder.ITestApplicationBuilder! testApplicationBuilder, string![]! _) -> void diff --git a/src/Platform/Microsoft.Testing.Extensions.MSBuild/PublicAPI.Unshipped.txt b/src/Platform/Microsoft.Testing.Extensions.MSBuild/PublicAPI.Unshipped.txt new file mode 100644 index 0000000000..7dc5c58110 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.MSBuild/PublicAPI.Unshipped.txt @@ -0,0 +1 @@ +#nullable enable diff --git a/src/Platform/Microsoft.Testing.Extensions.Retry/BannedSymbols.txt b/src/Platform/Microsoft.Testing.Extensions.Retry/BannedSymbols.txt index 5bc5c2fb96..7be39f73d1 100644 --- a/src/Platform/Microsoft.Testing.Extensions.Retry/BannedSymbols.txt +++ b/src/Platform/Microsoft.Testing.Extensions.Retry/BannedSymbols.txt @@ -6,5 +6,7 @@ M:System.Threading.Tasks.Task.WhenAll(System.Threading.Tasks.Task[]); Use 'ITask 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); Use 'RoslynDebug.Assert' instead M:System.Diagnostics.Debug.Assert(System.Boolean,System.String); Use 'RoslynDebug.Assert' instead +T:System.File; Use 'IFileSystem' instead +T:System.Directory; Use 'IFileSystem' instead diff --git a/src/Platform/Microsoft.Testing.Extensions.Retry/Microsoft.Testing.Extensions.Retry.csproj b/src/Platform/Microsoft.Testing.Extensions.Retry/Microsoft.Testing.Extensions.Retry.csproj index 9af602da35..017a33f007 100644 --- a/src/Platform/Microsoft.Testing.Extensions.Retry/Microsoft.Testing.Extensions.Retry.csproj +++ b/src/Platform/Microsoft.Testing.Extensions.Retry/Microsoft.Testing.Extensions.Retry.csproj @@ -43,21 +43,6 @@ This package extends Microsoft Testing Platform to provide a retry policy system - - - True - True - ExtensionResources.resx - - - - - - ResXFileCodeGenerator - ExtensionResources.Designer.cs - - - diff --git a/src/Platform/Microsoft.Testing.Extensions.Retry/Resources/ExtensionResources.Designer.cs b/src/Platform/Microsoft.Testing.Extensions.Retry/Resources/ExtensionResources.Designer.cs deleted file mode 100644 index 79b07ef2dc..0000000000 --- a/src/Platform/Microsoft.Testing.Extensions.Retry/Resources/ExtensionResources.Designer.cs +++ /dev/null @@ -1,284 +0,0 @@ -//------------------------------------------------------------------------------ -// -// 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.Policy.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.Policy.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 Failed to create retries directory due to collisions in '{0}' despite re-trying.. - /// - internal static string FailedToCreateRetryDirectoryBecauseOfCollision { - get { - return ResourceManager.GetString("FailedToCreateRetryDirectoryBecauseOfCollision", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Failure threshold policy is enabled, failed tests will not be restarted.. - /// - internal static string FailureThresholdPolicy { - get { - return ResourceManager.GetString("FailureThresholdPolicy", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Maximum failed tests threshold is {0} and {1} tests failed. - /// - internal static string FailureThresholdPolicyMaxCount { - get { - return ResourceManager.GetString("FailureThresholdPolicyMaxCount", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Percentage failed threshold is {0}% and {1}% tests failed ({2}/{3}). - /// - internal static string FailureThresholdPolicyMaxPercentage { - get { - return ResourceManager.GetString("FailureThresholdPolicyMaxPercentage", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to - ///===================== - ///Moving last attempt asset files to the default result directory - ///===================== - ///. - /// - internal static string MoveFiles { - get { - return ResourceManager.GetString("MoveFiles", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Moving file '{0}' to '{1}'. - /// - internal static string MovingFileToLocation { - get { - return ResourceManager.GetString("MovingFileToLocation", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Failed to start process '{0}'. - /// - internal static string RetryFailedTestsCannotStartProcessErrorMessage { - get { - return ResourceManager.GetString("RetryFailedTestsCannotStartProcessErrorMessage", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Retry failed tests feature allows to restart test execution upon failure.. - /// - internal static string RetryFailedTestsExtensionDescription { - get { - return ResourceManager.GetString("RetryFailedTestsExtensionDescription", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Retry failed tests. - /// - internal static string RetryFailedTestsExtensionDisplayName { - get { - return ResourceManager.GetString("RetryFailedTestsExtensionDisplayName", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Retry failed tests only works with builders of type 'Microsoft.Testing.Platform.Builder.TestApplicationBuilder'. - /// - internal static string RetryFailedTestsInvalidTestApplicationBuilderErrorMessage { - get { - return ResourceManager.GetString("RetryFailedTestsInvalidTestApplicationBuilderErrorMessage", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Disable retry mechanism if the percentage of failed tests is greater than the specified value. - /// - internal static string RetryFailedTestsMaxPercentageOptionDescription { - get { - return ResourceManager.GetString("RetryFailedTestsMaxPercentageOptionDescription", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Disable retry mechanism if the number of failed tests is greater than the specified value. - /// - internal static string RetryFailedTestsMaxTestsOptionDescription { - get { - return ResourceManager.GetString("RetryFailedTestsMaxTestsOptionDescription", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Retry failed tests feature is not supported in hot reload mode. - /// - internal static string RetryFailedTestsNotSupportedInHotReloadErrorMessage { - get { - return ResourceManager.GetString("RetryFailedTestsNotSupportedInHotReloadErrorMessage", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Retry failed tests feature is not supported in server mode. - /// - internal static string RetryFailedTestsNotSupportedInServerModeErrorMessage { - get { - return ResourceManager.GetString("RetryFailedTestsNotSupportedInServerModeErrorMessage", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Enable retry failed tests. - /// - internal static string RetryFailedTestsOptionDescription { - get { - return ResourceManager.GetString("RetryFailedTestsOptionDescription", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Option '{0}' requires option '{1}' to be specified. - /// - internal static string RetryFailedTestsOptionIsMissingErrorMessage { - get { - return ResourceManager.GetString("RetryFailedTestsOptionIsMissingErrorMessage", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Option '{0}' expects a single integer argument. - /// - internal static string RetryFailedTestsOptionSingleIntegerArgumentErrorMessage { - get { - return ResourceManager.GetString("RetryFailedTestsOptionSingleIntegerArgumentErrorMessage", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Options '{0}' and '{1}' cannot be used together. - /// - internal static string RetryFailedTestsPercentageAndCountCannotBeMixedErrorMessage { - get { - return ResourceManager.GetString("RetryFailedTestsPercentageAndCountCannotBeMixedErrorMessage", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Test host process exited before the retry service could connect to it. Exit code: {0}. - /// - internal static string TestHostProcessExitedBeforeRetryCouldConnect { - get { - return ResourceManager.GetString("TestHostProcessExitedBeforeRetryCouldConnect", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to - ///===================== - ///Tests suite completed successfully in {0} attempts - ///=====================. - /// - internal static string TestSuiteCompletedSuccessfully { - get { - return ResourceManager.GetString("TestSuiteCompletedSuccessfully", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to - ///===================== - ///Tests suite failed, total failed tests: {0}, exit code: {1}, attempt: {2}/{3} - ///===================== - ///. - /// - internal static string TestSuiteFailed { - get { - return ResourceManager.GetString("TestSuiteFailed", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to - ///===================== - ///Tests suite failed in all {0} attempts - ///=====================. - /// - internal static string TestSuiteFailedInAllAttempts { - get { - return ResourceManager.GetString("TestSuiteFailedInAllAttempts", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Test suite failed with and exit code different that 2 (failed tests). Failure related to an unexpected condition. Exit code '{0}'. - /// - internal static string TestSuiteFailedWithWrongExitCode { - get { - return ResourceManager.GetString("TestSuiteFailedWithWrongExitCode", resourceCulture); - } - } - } -} diff --git a/src/Platform/Microsoft.Testing.Extensions.Retry/RetryCommandLineOptionsProvider.cs b/src/Platform/Microsoft.Testing.Extensions.Retry/RetryCommandLineOptionsProvider.cs index 954fb4dfe9..5a5e521ca2 100644 --- a/src/Platform/Microsoft.Testing.Extensions.Retry/RetryCommandLineOptionsProvider.cs +++ b/src/Platform/Microsoft.Testing.Extensions.Retry/RetryCommandLineOptionsProvider.cs @@ -24,9 +24,8 @@ internal sealed class RetryCommandLineOptionsProvider : ICommandLineOptionsProvi public string Description => ExtensionResources.RetryFailedTestsExtensionDescription; - public IReadOnlyCollection GetCommandLineOptions() - => new CommandLineOption[] - { + public IReadOnlyCollection GetCommandLineOptions() => + [ // Hide the extension for now, we will add tests and we will re-enable when will be good. // We'd like to have some iteration in prod with our dogfooders before. new(RetryFailedTestsOptionName, ExtensionResources.RetryFailedTestsOptionDescription, ArgumentArity.ExactlyOne, false, isBuiltIn: true), @@ -34,8 +33,8 @@ public IReadOnlyCollection GetCommandLineOptions() new(RetryFailedTestsMaxTestsOptionName, ExtensionResources.RetryFailedTestsMaxTestsOptionDescription, ArgumentArity.ExactlyOne, false, isBuiltIn: true), // Hidden internal args - new(RetryFailedTestsPipeNameOptionName, "Communication between the test host and the retry infra.", ArgumentArity.ExactlyOne, isHidden: true, isBuiltIn: true), - }; + new(RetryFailedTestsPipeNameOptionName, "Communication between the test host and the retry infra.", ArgumentArity.ExactlyOne, isHidden: true, isBuiltIn: true) + ]; public Task IsEnabledAsync() => Task.FromResult(true); diff --git a/src/Platform/Microsoft.Testing.Extensions.Retry/RetryDataConsumer.cs b/src/Platform/Microsoft.Testing.Extensions.Retry/RetryDataConsumer.cs index 8b92f00072..2c1b46b798 100644 --- a/src/Platform/Microsoft.Testing.Extensions.Retry/RetryDataConsumer.cs +++ b/src/Platform/Microsoft.Testing.Extensions.Retry/RetryDataConsumer.cs @@ -28,7 +28,7 @@ public RetryDataConsumer(IServiceProvider serviceProvider) _commandLineOptions = _serviceProvider.GetCommandLineOptions(); } - public Type[] DataTypesConsumed => new[] { typeof(TestNodeUpdateMessage) }; + public Type[] DataTypesConsumed => [typeof(TestNodeUpdateMessage)]; public string Uid => nameof(RetryDataConsumer); @@ -51,7 +51,7 @@ public async Task ConsumeAsync(IDataProducer dataProducer, IData value, Cancella { ApplicationStateGuard.Ensure(_retryFailedTestsLifecycleCallbacks is not null); ApplicationStateGuard.Ensure(_retryFailedTestsLifecycleCallbacks.Client is not null); - await _retryFailedTestsLifecycleCallbacks.Client.RequestReplyAsync(new FailedTestRequest(testNodeUpdateMessage.TestNode.Uid), cancellationToken); + await _retryFailedTestsLifecycleCallbacks.Client.RequestReplyAsync(new FailedTestRequest(testNodeUpdateMessage.TestNode.Uid), cancellationToken).ConfigureAwait(false); } if (Array.IndexOf(TestNodePropertiesCategories.WellKnownTestNodeTestRunOutcomeProperties, nodeState.GetType()) != -1) @@ -64,7 +64,7 @@ public async Task OnTestSessionFinishingAsync(SessionUid sessionUid, Cancellatio { ApplicationStateGuard.Ensure(_retryFailedTestsLifecycleCallbacks is not null); ApplicationStateGuard.Ensure(_retryFailedTestsLifecycleCallbacks.Client is not null); - await _retryFailedTestsLifecycleCallbacks.Client.RequestReplyAsync(new TotalTestsRunRequest(_totalTests), cancellationToken); + await _retryFailedTestsLifecycleCallbacks.Client.RequestReplyAsync(new TotalTestsRunRequest(_totalTests), cancellationToken).ConfigureAwait(false); } public Task OnTestSessionStartingAsync(SessionUid sessionUid, CancellationToken cancellationToken) @@ -76,7 +76,7 @@ public Task IsEnabledAsync() public async Task InitializeAsync() { - if (await IsEnabledAsync()) + if (await IsEnabledAsync().ConfigureAwait(false)) { _retryFailedTestsLifecycleCallbacks = _serviceProvider.GetRequiredService(); } diff --git a/src/Platform/Microsoft.Testing.Extensions.Retry/RetryExecutionFilterFactory.cs b/src/Platform/Microsoft.Testing.Extensions.Retry/RetryExecutionFilterFactory.cs index 1beb0961e0..c2b9b8c13a 100644 --- a/src/Platform/Microsoft.Testing.Extensions.Retry/RetryExecutionFilterFactory.cs +++ b/src/Platform/Microsoft.Testing.Extensions.Retry/RetryExecutionFilterFactory.cs @@ -38,13 +38,12 @@ public Task IsEnabledAsync() _retryFailedTestsLifecycleCallbacks = _serviceProvider.GetRequiredService(); if (_retryFailedTestsLifecycleCallbacks.FailedTestsIDToRetry?.Length > 0) { - return (true, new TestNodeUidListFilter(_retryFailedTestsLifecycleCallbacks.FailedTestsIDToRetry - .Select(x => new TestNodeUid(x)).ToArray())); + return (true, new TestNodeUidListFilter([.. _retryFailedTestsLifecycleCallbacks.FailedTestsIDToRetry.Select(x => new TestNodeUid(x))])); } else { ConsoleTestExecutionFilterFactory consoleTestExecutionFilterFactory = new(_commandLineOptions); - return await consoleTestExecutionFilterFactory.TryCreateAsync(); + return await consoleTestExecutionFilterFactory.TryCreateAsync().ConfigureAwait(false); } } } diff --git a/src/Platform/Microsoft.Testing.Extensions.Retry/RetryExtensions.cs b/src/Platform/Microsoft.Testing.Extensions.Retry/RetryExtensions.cs index 0a048a39c9..544672fc03 100644 --- a/src/Platform/Microsoft.Testing.Extensions.Retry/RetryExtensions.cs +++ b/src/Platform/Microsoft.Testing.Extensions.Retry/RetryExtensions.cs @@ -23,7 +23,7 @@ public static void AddRetryProvider(this ITestApplicationBuilder builder) { builder.CommandLine.AddProvider(() => new RetryCommandLineOptionsProvider()); - builder.TestHost.AddTestApplicationLifecycleCallbacks(serviceProvider + builder.TestHost.AddTestHostApplicationLifetime(serviceProvider => new RetryLifecycleCallbacks(serviceProvider)); CompositeExtensionFactory compositeExtensionFactory diff --git a/src/Platform/Microsoft.Testing.Extensions.Retry/RetryFailedTestsPipeServer.cs b/src/Platform/Microsoft.Testing.Extensions.Retry/RetryFailedTestsPipeServer.cs index 21032e0497..0702dba543 100644 --- a/src/Platform/Microsoft.Testing.Extensions.Retry/RetryFailedTestsPipeServer.cs +++ b/src/Platform/Microsoft.Testing.Extensions.Retry/RetryFailedTestsPipeServer.cs @@ -19,7 +19,7 @@ internal sealed class RetryFailedTestsPipeServer : IDisposable public RetryFailedTestsPipeServer(IServiceProvider serviceProvider, string[] failedTests, ILogger logger) { - _pipeNameDescription = NamedPipeServer.GetPipeName(Guid.NewGuid().ToString("N")); + _pipeNameDescription = NamedPipeServer.GetPipeName(Guid.NewGuid().ToString("N"), serviceProvider.GetEnvironment()); logger.LogTrace($"Retry server pipe name: '{_pipeNameDescription.Name}'"); _singleConnectionNamedPipeServer = new NamedPipeServer(_pipeNameDescription, CallbackAsync, serviceProvider.GetEnvironment(), @@ -54,7 +54,7 @@ private Task CallbackAsync(IRequest request) { if (request is FailedTestRequest failed) { - FailedUID ??= new(); + FailedUID ??= []; FailedUID.Add(failed.Uid); return Task.FromResult((IResponse)VoidResponse.CachedInstance); } diff --git a/src/Platform/Microsoft.Testing.Extensions.Retry/RetryLifecycleCallbacks.cs b/src/Platform/Microsoft.Testing.Extensions.Retry/RetryLifecycleCallbacks.cs index 6d019d9fd1..91c7fda0a1 100644 --- a/src/Platform/Microsoft.Testing.Extensions.Retry/RetryLifecycleCallbacks.cs +++ b/src/Platform/Microsoft.Testing.Extensions.Retry/RetryLifecycleCallbacks.cs @@ -16,7 +16,7 @@ namespace Microsoft.Testing.Extensions.Policy; -internal sealed class RetryLifecycleCallbacks : ITestApplicationLifecycleCallbacks, +internal sealed class RetryLifecycleCallbacks : ITestHostApplicationLifetime, #if NETCOREAPP IAsyncDisposable #else @@ -63,9 +63,9 @@ public async Task BeforeRunAsync(CancellationToken cancellationToken) Client.RegisterSerializer(new GetListOfFailedTestsRequestSerializer(), typeof(GetListOfFailedTestsRequest)); Client.RegisterSerializer(new GetListOfFailedTestsResponseSerializer(), typeof(GetListOfFailedTestsResponse)); Client.RegisterSerializer(new TotalTestsRunRequestSerializer(), typeof(TotalTestsRunRequest)); - await Client.ConnectAsync(cancellationToken); + await Client.ConnectAsync(cancellationToken).ConfigureAwait(false); - GetListOfFailedTestsResponse result = await Client.RequestReplyAsync(new GetListOfFailedTestsRequest(), cancellationToken); + GetListOfFailedTestsResponse result = await Client.RequestReplyAsync(new GetListOfFailedTestsRequest(), cancellationToken).ConfigureAwait(false); FailedTestsIDToRetry = result.FailedTestIds; } @@ -80,7 +80,7 @@ public async ValueTask DisposeAsync() { if (Client is not null) { - await Client.DisposeAsync(); + await Client.DisposeAsync().ConfigureAwait(false); } } #else diff --git a/src/Platform/Microsoft.Testing.Extensions.Retry/RetryOrchestrator.cs b/src/Platform/Microsoft.Testing.Extensions.Retry/RetryOrchestrator.cs index 59df6004f2..eab108c33f 100644 --- a/src/Platform/Microsoft.Testing.Extensions.Retry/RetryOrchestrator.cs +++ b/src/Platform/Microsoft.Testing.Extensions.Retry/RetryOrchestrator.cs @@ -17,11 +17,13 @@ internal sealed class RetryOrchestrator : ITestHostOrchestrator, IOutputDeviceDa { private readonly IServiceProvider _serviceProvider; private readonly ICommandLineOptions _commandLineOptions; + private readonly IFileSystem _fileSystem; public RetryOrchestrator(IServiceProvider serviceProvider) { _serviceProvider = serviceProvider; _commandLineOptions = _serviceProvider.GetCommandLineOptions(); + _fileSystem = _serviceProvider.GetFileSystem(); } public string Uid => nameof(RetryOrchestrator); @@ -35,21 +37,21 @@ public RetryOrchestrator(IServiceProvider serviceProvider) public Task IsEnabledAsync() => Task.FromResult(_commandLineOptions.IsOptionSet(RetryCommandLineOptionsProvider.RetryFailedTestsOptionName)); - private static string CreateRetriesDirectory(string resultDirectory) + private string CreateRetriesDirectory(string resultDirectory) { Exception? lastException = null; // Quite arbitrary. Keep trying to create the directory for 10 times. for (int i = 0; i < 10; i++) { string retryRootFolder = Path.Combine(resultDirectory, "Retries", RandomId.Next()); - if (Directory.Exists(retryRootFolder)) + if (_fileSystem.ExistDirectory(retryRootFolder)) { continue; } try { - Directory.CreateDirectory(retryRootFolder); + _fileSystem.CreateDirectory(retryRootFolder); return retryRootFolder; } catch (IOException ex) @@ -73,11 +75,14 @@ public async Task OrchestrateTestHostExecutionAsync() throw new InvalidOperationException(ExtensionResources.RetryFailedTestsNotSupportedInServerModeErrorMessage); } - if (IsHotReloadEnabled(_serviceProvider.GetEnvironment())) + IEnvironment environment = _serviceProvider.GetEnvironment(); + if (IsHotReloadEnabled(environment)) { throw new InvalidOperationException(ExtensionResources.RetryFailedTestsNotSupportedInHotReloadErrorMessage); } + environment.SetEnvironmentVariable(EnvironmentVariableConstants.TESTINGPLATFORM_TRX_TESTRUN_ID, Guid.NewGuid().ToString("N")); + ILogger logger = _serviceProvider.GetLoggerFactory().CreateLogger(); IConfiguration configuration = _serviceProvider.GetConfiguration(); @@ -93,8 +98,8 @@ public async Task OrchestrateTestHostExecutionAsync() int userMaxRetryCount = int.Parse(cmdRetries[0], CultureInfo.InvariantCulture); // Find out the retry args index inside the arguments to after cleanup the command line when we restart - List indexToCleanup = new(); - string[] executableArguments = executableInfo.Arguments.ToArray(); + List indexToCleanup = []; + string[] executableArguments = [.. executableInfo.Arguments]; int argIndex = GetOptionArgumentIndex(RetryCommandLineOptionsProvider.RetryFailedTestsOptionName, executableArguments); if (argIndex < 0) { @@ -128,12 +133,12 @@ public async Task OrchestrateTestHostExecutionAsync() // Override the result directory with the attempt one string resultDirectory = configuration.GetTestResultDirectory(); - List exitCodes = new(); + List exitCodes = []; IOutputDevice outputDevice = _serviceProvider.GetOutputDevice(); IFileSystem fileSystem = _serviceProvider.GetFileSystem(); int attemptCount = 0; - List finalArguments = new(); + List finalArguments = []; string[]? lastListOfFailedId = null; string? currentTryResultFolder = null; bool thresholdPolicyKickedIn = false; @@ -160,29 +165,34 @@ public async Task OrchestrateTestHostExecutionAsync() finalArguments.Add(currentTryResultFolder); // Prepare the pipeserver - using RetryFailedTestsPipeServer retryFailedTestsPipeServer = new(_serviceProvider, lastListOfFailedId ?? Array.Empty(), logger); + using RetryFailedTestsPipeServer retryFailedTestsPipeServer = new(_serviceProvider, lastListOfFailedId ?? [], logger); finalArguments.Add($"--{RetryCommandLineOptionsProvider.RetryFailedTestsPipeNameOptionName}"); finalArguments.Add(retryFailedTestsPipeServer.PipeName); - // Prepare the process start - ProcessStartInfo processStartInfo = new() +#if NET8_0_OR_GREATER + // On net8.0+, we can pass the arguments as a collection directly to ProcessStartInfo. + // When passing the collection, it's expected to be unescaped, so we pass what we have directly. + List arguments = finalArguments; +#else + // Current target framework (.NET Framework and .NET Standard 2.0) only supports arguments as a single string. + // In this case, escaping is essential. For example, one of the arguments could already contain spaces. + // PasteArguments is borrowed from dotnet/runtime. + var builder = new StringBuilder(); + foreach (string arg in finalArguments) { - FileName = executableInfo.FilePath, -#if !NETCOREAPP - UseShellExecute = false, + PasteArguments.AppendArgument(builder, arg); + } + + string arguments = builder.ToString(); #endif - }; - foreach (string argument in finalArguments) + // Prepare the process start + ProcessStartInfo processStartInfo = new(executableInfo.FilePath, arguments) { -#if !NETCOREAPP - processStartInfo.Arguments += argument + " "; -#else - processStartInfo.ArgumentList.Add(argument); -#endif - } + UseShellExecute = false, + }; - await logger.LogDebugAsync($"Starting test host process, attempt {attemptCount}/{userMaxRetryCount}"); + await logger.LogDebugAsync($"Starting test host process, attempt {attemptCount}/{userMaxRetryCount}").ConfigureAwait(false); IProcess testHostProcess = _serviceProvider.GetProcessHandler().Start(processStartInfo) ?? throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, ExtensionResources.RetryFailedTestsCannotStartProcessErrorMessage, processStartInfo.FileName)); @@ -198,39 +208,39 @@ public async Task OrchestrateTestHostExecutionAsync() using (var linkedToken = CancellationTokenSource.CreateLinkedTokenSource(timeout.Token, _serviceProvider.GetTestApplicationCancellationTokenSource().CancellationToken)) using (var linkedToken2 = CancellationTokenSource.CreateLinkedTokenSource(linkedToken.Token, processExitedCancellationToken.Token)) { - await logger.LogDebugAsync("Wait connection from the test host process"); + await logger.LogDebugAsync("Wait connection from the test host process").ConfigureAwait(false); try { #if NETCOREAPP - await retryFailedTestsPipeServer.WaitForConnectionAsync(linkedToken2.Token); + await retryFailedTestsPipeServer.WaitForConnectionAsync(linkedToken2.Token).ConfigureAwait(false); #else // We don't know why but if the cancellation is called quickly in line 171 for netfx we stuck sometime here, like if // the token we pass to the named pipe is not "correctly" verified inside the pipe implementation self. // We fallback with our custom agnostic cancellation mechanism in that case. // We see it happen only in .NET FX and not in .NET Core so for now we don't do it for core. - await retryFailedTestsPipeServer.WaitForConnectionAsync(linkedToken2.Token).WithCancellationAsync(linkedToken2.Token); + await retryFailedTestsPipeServer.WaitForConnectionAsync(linkedToken2.Token).WithCancellationAsync(linkedToken2.Token).ConfigureAwait(false); #endif } catch (OperationCanceledException) when (processExitedCancellationToken.IsCancellationRequested) { - await outputDevice.DisplayAsync(this, new ErrorMessageOutputDeviceData(string.Format(CultureInfo.InvariantCulture, ExtensionResources.TestHostProcessExitedBeforeRetryCouldConnect, testHostProcess.ExitCode))); + await outputDevice.DisplayAsync(this, new ErrorMessageOutputDeviceData(string.Format(CultureInfo.InvariantCulture, ExtensionResources.TestHostProcessExitedBeforeRetryCouldConnect, testHostProcess.ExitCode))).ConfigureAwait(false); return ExitCodes.GenericFailure; } } - await testHostProcess.WaitForExitAsync(); + await testHostProcess.WaitForExitAsync().ConfigureAwait(false); exitCodes.Add(testHostProcess.ExitCode); if (testHostProcess.ExitCode != ExitCodes.Success) { if (testHostProcess.ExitCode != ExitCodes.AtLeastOneTestFailed) { - await outputDevice.DisplayAsync(this, new WarningMessageOutputDeviceData(string.Format(CultureInfo.InvariantCulture, ExtensionResources.TestSuiteFailedWithWrongExitCode, testHostProcess.ExitCode))); + await outputDevice.DisplayAsync(this, new WarningMessageOutputDeviceData(string.Format(CultureInfo.InvariantCulture, ExtensionResources.TestSuiteFailedWithWrongExitCode, testHostProcess.ExitCode))).ConfigureAwait(false); retryInterrupted = true; break; } - await outputDevice.DisplayAsync(this, new WarningMessageOutputDeviceData(string.Format(CultureInfo.InvariantCulture, ExtensionResources.TestSuiteFailed, retryFailedTestsPipeServer.FailedUID?.Count ?? 0, testHostProcess.ExitCode, attemptCount, userMaxRetryCount + 1))); + await outputDevice.DisplayAsync(this, new WarningMessageOutputDeviceData(string.Format(CultureInfo.InvariantCulture, ExtensionResources.TestSuiteFailed, retryFailedTestsPipeServer.FailedUID?.Count ?? 0, testHostProcess.ExitCode, attemptCount, userMaxRetryCount + 1))).ConfigureAwait(false); // Check thresholds if (attemptCount == 1) @@ -269,7 +279,7 @@ public async Task OrchestrateTestHostExecutionAsync() explanation.AppendLine(string.Format(CultureInfo.InvariantCulture, ExtensionResources.FailureThresholdPolicyMaxCount, maxCount, retryFailedTestsPipeServer.FailedUID!.Count)); } - await outputDevice.DisplayAsync(this, new ErrorMessageOutputDeviceData(explanation.ToString())); + await outputDevice.DisplayAsync(this, new ErrorMessageOutputDeviceData(explanation.ToString())).ConfigureAwait(false); break; } } @@ -288,20 +298,20 @@ public async Task OrchestrateTestHostExecutionAsync() { if (exitCodes[^1] != ExitCodes.Success) { - await outputDevice.DisplayAsync(this, new ErrorMessageOutputDeviceData(string.Format(CultureInfo.InvariantCulture, ExtensionResources.TestSuiteFailedInAllAttempts, userMaxRetryCount + 1))); + await outputDevice.DisplayAsync(this, new ErrorMessageOutputDeviceData(string.Format(CultureInfo.InvariantCulture, ExtensionResources.TestSuiteFailedInAllAttempts, userMaxRetryCount + 1))).ConfigureAwait(false); } else { - await outputDevice.DisplayAsync(this, new FormattedTextOutputDeviceData(string.Format(CultureInfo.InvariantCulture, ExtensionResources.TestSuiteCompletedSuccessfully, attemptCount)) { ForegroundColor = new SystemConsoleColor { ConsoleColor = ConsoleColor.Green } }); + await outputDevice.DisplayAsync(this, new FormattedTextOutputDeviceData(string.Format(CultureInfo.InvariantCulture, ExtensionResources.TestSuiteCompletedSuccessfully, attemptCount)) { ForegroundColor = new SystemConsoleColor { ConsoleColor = ConsoleColor.DarkGreen } }).ConfigureAwait(false); } } ApplicationStateGuard.Ensure(currentTryResultFolder is not null); - string[] filesToMove = Directory.GetFiles(currentTryResultFolder, "*.*", SearchOption.AllDirectories); + string[] filesToMove = _fileSystem.GetFiles(currentTryResultFolder, "*.*", SearchOption.AllDirectories); if (filesToMove.Length > 0) { - await outputDevice.DisplayAsync(this, new TextOutputDeviceData(ExtensionResources.MoveFiles)); + await outputDevice.DisplayAsync(this, new TextOutputDeviceData(ExtensionResources.MoveFiles)).ConfigureAwait(false); // Move last attempt assets foreach (string file in filesToMove) @@ -309,14 +319,14 @@ public async Task OrchestrateTestHostExecutionAsync() string finalFileLocation = file.Replace(currentTryResultFolder, resultDirectory); // Create the directory if missing - Directory.CreateDirectory(Path.GetDirectoryName(finalFileLocation)!); + fileSystem.CreateDirectory(Path.GetDirectoryName(finalFileLocation)!); - await outputDevice.DisplayAsync(this, new TextOutputDeviceData(string.Format(CultureInfo.InvariantCulture, ExtensionResources.MovingFileToLocation, file, finalFileLocation))); + await outputDevice.DisplayAsync(this, new TextOutputDeviceData(string.Format(CultureInfo.InvariantCulture, ExtensionResources.MovingFileToLocation, file, finalFileLocation))).ConfigureAwait(false); #if NETCOREAPP - File.Move(file, finalFileLocation, overwrite: true); + fileSystem.MoveFile(file, finalFileLocation, overwrite: true); #else - File.Copy(file, finalFileLocation, overwrite: true); - File.Delete(file); + fileSystem.CopyFile(file, finalFileLocation, overwrite: true); + fileSystem.DeleteFile(file); #endif } } diff --git a/src/Platform/Microsoft.Testing.Extensions.Telemetry/AppInsightTelemetryClient.cs b/src/Platform/Microsoft.Testing.Extensions.Telemetry/AppInsightTelemetryClient.cs index fe27fd2eb2..33d9a0ba3c 100644 --- a/src/Platform/Microsoft.Testing.Extensions.Telemetry/AppInsightTelemetryClient.cs +++ b/src/Platform/Microsoft.Testing.Extensions.Telemetry/AppInsightTelemetryClient.cs @@ -12,14 +12,13 @@ internal sealed class AppInsightTelemetryClient : ITelemetryClient 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); + var config = TelemetryConfiguration.CreateDefault(); + config.ConnectionString = $"InstrumentationKey={InstrumentationKey};IngestionEndpoint={TelemetryServiceEndpoint}"; + _telemetryClient = new TelemetryClient(config); _telemetryClient.Context.Session.Id = currentSessionId; _telemetryClient.Context.Device.OperatingSystem = osVersion; } diff --git a/src/Platform/Microsoft.Testing.Extensions.Telemetry/AppInsightsProvider.cs b/src/Platform/Microsoft.Testing.Extensions.Telemetry/AppInsightsProvider.cs index 959180a64a..fd9926502d 100644 --- a/src/Platform/Microsoft.Testing.Extensions.Telemetry/AppInsightsProvider.cs +++ b/src/Platform/Microsoft.Testing.Extensions.Telemetry/AppInsightsProvider.cs @@ -108,7 +108,7 @@ public AppInsightsProvider( #else // Keep the custom thread to avoid to waste one from thread pool. // We have some await but we should stay on the custom thread if not for special cases like trace log or exception. - _payloads = new(); + _payloads = []; _telemetryTask = _task.RunLongRunning(IngestLoopAsync, "Telemetry AppInsightsProvider", _testApplicationCancellationTokenSource.CancellationToken); #endif @@ -131,7 +131,7 @@ private async Task IngestLoopAsync() { _client = null; - await _logger.LogErrorAsync("Failed to initialize telemetry client", e); + await _logger.LogErrorAsync("Failed to initialize telemetry client", e).ConfigureAwait(false); return; } @@ -140,9 +140,9 @@ private async Task IngestLoopAsync() try { #if NETCOREAPP - while (await _payloads.Reader.WaitToReadAsync(_flushTimeoutOrStop.Token)) + while (await _payloads.Reader.WaitToReadAsync(_flushTimeoutOrStop.Token).ConfigureAwait(false)) { - (string eventName, IDictionary paramsMap) = await _payloads.Reader.ReadAsync(); + (string eventName, IDictionary paramsMap) = await _payloads.Reader.ReadAsync().ConfigureAwait(false); #else foreach ((string eventName, IDictionary paramsMap) in _payloads.GetConsumingEnumerable(_flushTimeoutOrStop.Token)) { @@ -205,7 +205,7 @@ private async Task IngestLoopAsync() builder.AppendLine(CultureInfo.InvariantCulture, $" {key}: {value.ToString("f", CultureInfo.InvariantCulture)}"); } - await _logger.LogTraceAsync(builder.ToString()); + await _logger.LogTraceAsync(builder.ToString()).ConfigureAwait(false); } try @@ -219,7 +219,7 @@ private async Task IngestLoopAsync() // We could do better back-pressure. if (_logger.IsEnabled(LogLevel.Error) && (!lastLoggedError.HasValue || (lastLoggedError.Value - _clock.UtcNow).TotalSeconds > 3)) { - await _logger.LogErrorAsync("Error during telemetry report.", ex); + await _logger.LogErrorAsync("Error during telemetry report.", ex).ConfigureAwait(false); lastLoggedError = _clock.UtcNow; } } @@ -260,10 +260,10 @@ private static void AssertHashed(string key, string value) } #if NET7_0_OR_GREATER - [System.Text.RegularExpressions.GeneratedRegex("[a-f0-9]{64}")] - private static partial System.Text.RegularExpressions.Regex GetValidHashPattern(); + [GeneratedRegex("[a-f0-9]{64}")] + private static partial Regex GetValidHashPattern(); #else - private static System.Text.RegularExpressions.Regex GetValidHashPattern() + private static Regex GetValidHashPattern() => new("[a-f0-9]{64}"); #endif #endif @@ -275,7 +275,7 @@ private static System.Text.RegularExpressions.Regex GetValidHashPattern() Task LogEventAsync(string eventName, IDictionary paramsMap) { #if NETCOREAPP - await _payloads.Writer.WriteAsync((eventName, paramsMap)); + await _payloads.Writer.WriteAsync((eventName, paramsMap)).ConfigureAwait(false); #else _payloads.Add((eventName, paramsMap)); return Task.CompletedTask; @@ -320,12 +320,12 @@ public async ValueTask DisposeAsync() int flushForSeconds = 3; try { - await _telemetryTask.TimeoutAfterAsync(TimeSpan.FromSeconds(flushForSeconds)); + await _telemetryTask.TimeoutAfterAsync(TimeSpan.FromSeconds(flushForSeconds)).ConfigureAwait(false); } catch (TimeoutException) { - await _flushTimeoutOrStop.CancelAsync(); - await _logger.LogWarningAsync($"Telemetry task didn't flush after '{flushForSeconds}', some payload could be lost"); + await _flushTimeoutOrStop.CancelAsync().ConfigureAwait(false); + await _logger.LogWarningAsync($"Telemetry task didn't flush after '{flushForSeconds}', some payload could be lost").ConfigureAwait(false); } _isDisposed = true; diff --git a/src/Platform/Microsoft.Testing.Extensions.Telemetry/BannedSymbols.txt b/src/Platform/Microsoft.Testing.Extensions.Telemetry/BannedSymbols.txt index 469bd16cf9..0fcaeeec4b 100644 --- a/src/Platform/Microsoft.Testing.Extensions.Telemetry/BannedSymbols.txt +++ b/src/Platform/Microsoft.Testing.Extensions.Telemetry/BannedSymbols.txt @@ -1,4 +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); 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 index b90059537b..0ac25bde4a 100644 --- a/src/Platform/Microsoft.Testing.Extensions.Telemetry/CIEnvironmentDetectorForTelemetry.cs +++ b/src/Platform/Microsoft.Testing.Extensions.Telemetry/CIEnvironmentDetectorForTelemetry.cs @@ -33,8 +33,8 @@ internal sealed class CIEnvironmentDetectorForTelemetry ]; // Systems where every variable must be present and not-null before returning true - private static readonly string[][] AllNotNullVariables = new string[][] - { + private static readonly string[][] AllNotNullVariables = + [ // AWS CodeBuild - https://docs.aws.amazon.com/codebuild/latest/userguide/build-env-ref-env-vars.html ["CODEBUILD_BUILD_ID", "AWS_REGION"], @@ -43,7 +43,7 @@ internal sealed class CIEnvironmentDetectorForTelemetry // 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 = 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 index 7eca355606..bc59d4a00a 100644 --- a/src/Platform/Microsoft.Testing.Extensions.Telemetry/Microsoft.Testing.Extensions.Telemetry.csproj +++ b/src/Platform/Microsoft.Testing.Extensions.Telemetry/Microsoft.Testing.Extensions.Telemetry.csproj @@ -2,6 +2,7 @@ netstandard2.0;$(MicrosoftTestingTargetFrameworks) + $(NoWarn);NU1510 @@ -42,14 +43,14 @@ This package provides telemetry for the platform.]]> - - - + + + - + diff --git a/src/Platform/Microsoft.Testing.Extensions.TrxReport.Abstractions/BannedSymbols.txt b/src/Platform/Microsoft.Testing.Extensions.TrxReport.Abstractions/BannedSymbols.txt index ea8617fcb0..09d715c49c 100644 --- a/src/Platform/Microsoft.Testing.Extensions.TrxReport.Abstractions/BannedSymbols.txt +++ b/src/Platform/Microsoft.Testing.Extensions.TrxReport.Abstractions/BannedSymbols.txt @@ -6,5 +6,5 @@ M:System.Threading.Tasks.Task.WhenAll(System.Threading.Tasks.Task[]); Use 'ITask 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); 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/BannedSymbols.txt b/src/Platform/Microsoft.Testing.Extensions.TrxReport/BannedSymbols.txt index ea8617fcb0..09d715c49c 100644 --- a/src/Platform/Microsoft.Testing.Extensions.TrxReport/BannedSymbols.txt +++ b/src/Platform/Microsoft.Testing.Extensions.TrxReport/BannedSymbols.txt @@ -6,5 +6,5 @@ M:System.Threading.Tasks.Task.WhenAll(System.Threading.Tasks.Task[]); Use 'ITask 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); 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 index 0cfd477f7f..5aa0333965 100644 --- a/src/Platform/Microsoft.Testing.Extensions.TrxReport/Microsoft.Testing.Extensions.TrxReport.csproj +++ b/src/Platform/Microsoft.Testing.Extensions.TrxReport/Microsoft.Testing.Extensions.TrxReport.csproj @@ -54,10 +54,6 @@ This package extends Microsoft Testing Platform to provide TRX test reports.]]> - - - - diff --git a/src/Platform/Microsoft.Testing.Extensions.TrxReport/ToolTrxCompareFactory.cs b/src/Platform/Microsoft.Testing.Extensions.TrxReport/ToolTrxCompareFactory.cs index e88cb5b300..8cd1723dbb 100644 --- a/src/Platform/Microsoft.Testing.Extensions.TrxReport/ToolTrxCompareFactory.cs +++ b/src/Platform/Microsoft.Testing.Extensions.TrxReport/ToolTrxCompareFactory.cs @@ -12,10 +12,10 @@ namespace Microsoft.Testing.Extensions.TrxReport.Abstractions; internal sealed class ToolTrxCompareFactory : IExtension { /// - public string Uid { get; } = nameof(TrxCompareTool); + public string Uid => nameof(TrxCompareTool); /// - public string Version { get; } = AppVersion.DefaultSemVer; + public string Version => AppVersion.DefaultSemVer; /// public string DisplayName { get; } = ExtensionResources.TrxComparerToolDisplayName; diff --git a/src/Platform/Microsoft.Testing.Extensions.TrxReport/TrxCommandLine.cs b/src/Platform/Microsoft.Testing.Extensions.TrxReport/TrxCommandLine.cs index 51f36f19dc..db827e0d45 100644 --- a/src/Platform/Microsoft.Testing.Extensions.TrxReport/TrxCommandLine.cs +++ b/src/Platform/Microsoft.Testing.Extensions.TrxReport/TrxCommandLine.cs @@ -16,10 +16,10 @@ internal sealed class TrxReportGeneratorCommandLine : ICommandLineOptionsProvide public const string TrxReportFileNameOptionName = "report-trx-filename"; /// - public string Uid { get; } = nameof(TrxReportGeneratorCommandLine); + public string Uid => nameof(TrxReportGeneratorCommandLine); /// - public string Version { get; } = AppVersion.DefaultSemVer; + public string Version => AppVersion.DefaultSemVer; /// public string DisplayName { get; } = ExtensionResources.TrxReportGeneratorDisplayName; diff --git a/src/Platform/Microsoft.Testing.Extensions.TrxReport/TrxCompareTool.CommandLine.cs b/src/Platform/Microsoft.Testing.Extensions.TrxReport/TrxCompareTool.CommandLine.cs index bc131ffc4f..294e61c76a 100644 --- a/src/Platform/Microsoft.Testing.Extensions.TrxReport/TrxCompareTool.CommandLine.cs +++ b/src/Platform/Microsoft.Testing.Extensions.TrxReport/TrxCompareTool.CommandLine.cs @@ -31,7 +31,7 @@ public TrxCompareToolCommandLine(IExtension extension) public string Description => _extension.Description; /// - public string ToolName { get; } = TrxCompareTool.ToolName; + public string ToolName => TrxCompareTool.ToolName; /// public Task IsEnabledAsync() => Task.FromResult(true); diff --git a/src/Platform/Microsoft.Testing.Extensions.TrxReport/TrxCompareTool.cs b/src/Platform/Microsoft.Testing.Extensions.TrxReport/TrxCompareTool.cs index c79cb5c062..43e967f035 100644 --- a/src/Platform/Microsoft.Testing.Extensions.TrxReport/TrxCompareTool.cs +++ b/src/Platform/Microsoft.Testing.Extensions.TrxReport/TrxCompareTool.cs @@ -12,6 +12,8 @@ namespace Microsoft.Testing.Extensions.TrxReport.Abstractions; internal sealed class TrxCompareTool : ITool, IOutputDeviceDataProducer { + private record struct Trx(string TestName, string Outcome, string Storage); + public const string ToolName = "ms-trxcompare"; private readonly ICommandLineOptions _commandLineOptions; private readonly IExtension _extension; @@ -28,7 +30,7 @@ public TrxCompareTool(ICommandLineOptions commandLineOptions, IExtension extensi } /// - public string Name { get; } = ToolName; + public string Name => ToolName; /// public string Uid => _extension.Uid; @@ -55,13 +57,14 @@ public async Task RunAsync() 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(); + List baseLineResults = []; + List baseLineIssues = []; + List comparedResults = []; + List comparedIssues = []; await _task.WhenAll( - _task.Run(() => CollectEntriesAndErrors(baselineFilePaths[0], trxNamespace, baseLineResults, baseLineIssues)), - _task.Run(() => CollectEntriesAndErrors(comparedFilePaths[0], trxNamespace, comparedResults, comparedIssues))); + CollectEntriesAndErrorsAsync(baselineFilePaths[0], trxNamespace, baseLineResults, baseLineIssues), + CollectEntriesAndErrorsAsync(comparedFilePaths[0], trxNamespace, comparedResults, comparedIssues)) + .ConfigureAwait(false); StringBuilder outputBuilder = new(); AppendResultsAndIssues("Baseline", baselineFilePaths[0], baseLineResults, baseLineIssues, outputBuilder); @@ -69,33 +72,33 @@ await _task.WhenAll( if (AreMatchingTrxFiles(baseLineResults, comparedResults, outputBuilder)) { - await _outputDisplay.DisplayAsync(this, new TextOutputDeviceData(outputBuilder.ToString())); + await _outputDisplay.DisplayAsync(this, new TextOutputDeviceData(outputBuilder.ToString())).ConfigureAwait(false); return ExitCodes.Success; } else { - await _outputDisplay.DisplayAsync(this, new TextOutputDeviceData(outputBuilder.ToString())); + await _outputDisplay.DisplayAsync(this, new TextOutputDeviceData(outputBuilder.ToString())).ConfigureAwait(false); return ExitCodes.GenericFailure; } } private static bool AreMatchingTrxFiles( - List<(string TestName, string Outcome, string Storage)> baseLineResults, - List<(string TestName, string Outcome, string Storage)> comparedResults, + List baseLineResults, + List comparedResults, StringBuilder outputBuilder) { bool checkFailed = false; outputBuilder.AppendLine("--- Comparing TRX files ---"); - IEnumerable<((string TestName, string Outcome, string Storage), string Source)> trxEntries = + IEnumerable<(Trx, 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) + foreach ((Trx sourceTrx, string entrySource) in trxEntries) { string otherSource = entrySource == "baseline" ? "other" : "baseline"; - IEnumerable<(string MatchingTestName, string MatchingOutcome, string MatchingStorage)> matchingEntries = + IEnumerable matchingEntries = entrySource == "baseline" ? comparedResults.Where(x => x.TestName == sourceTrx.TestName) : baseLineResults.Where(x => x.TestName == sourceTrx.TestName); @@ -117,13 +120,13 @@ private static bool AreMatchingTrxFiles( continue; } - (string otherTestName, string otherOutcome, string otherStorage) = matchingEntries.First(); - if (sourceTrx.Outcome != otherOutcome) + Trx otherTrx = matchingEntries.First(); + if (sourceTrx.Outcome != otherTrx.Outcome) { checkFailed = true; outputBuilder.AppendLine( CultureInfo.InvariantCulture, - $" - Test '{sourceTrx.TestName}' has a different outcome. Got '{otherOutcome}', expected '{sourceTrx.Outcome}'"); + $" - Test '{sourceTrx.TestName}' has a different outcome. Got '{otherTrx.Outcome}', expected '{sourceTrx.Outcome}'"); } } @@ -141,7 +144,7 @@ private static bool AreMatchingTrxFiles( } private static void AppendResultsAndIssues(string category, string filePath, - List<(string TestName, string Outcome, string Storage)> results, List issues, StringBuilder outputBuilder) + List results, List issues, StringBuilder outputBuilder) { outputBuilder.AppendLine(CultureInfo.InvariantCulture, $"--- {category} ---"); outputBuilder.AppendLine(CultureInfo.InvariantCulture, $"File '{filePath}'"); @@ -174,9 +177,10 @@ private static void AppendResultsAndIssues(string category, string filePath, outputBuilder.AppendLine(); } - private static void CollectEntriesAndErrors(string trxFile, XNamespace ns, List<(string TestName, string Outcome, string Storage)> results, List issues) + private static async Task CollectEntriesAndErrorsAsync(string trxFile, XNamespace ns, List results, List issues) { - var trxTestRun = XElement.Parse(File.ReadAllText(trxFile)); + using FileStream stream = File.OpenRead(trxFile); + XElement trxTestRun = await XElement.LoadAsync(stream, LoadOptions.None, CancellationToken.None).ConfigureAwait(false); int testResultIndex = 0; foreach (XElement testResult in trxTestRun.Elements(ns + "Results").Elements(ns + "UnitTestResult")) { @@ -202,11 +206,10 @@ private static void CollectEntriesAndErrors(string trxFile, XNamespace ns, List< continue; } - XElement[] matchingUnitTestDefinitions = trxTestRun + XElement[] matchingUnitTestDefinitions = [.. trxTestRun .Elements(ns + "TestDefinitions") .Elements(ns + "UnitTest") - .Where(x => x.Attribute("id")?.Value == testId) - .ToArray(); + .Where(x => x.Attribute("id")?.Value == testId)]; if (matchingUnitTestDefinitions.Length > 1) { issues.Add($"Found more than one entry in 'TestDefinitions.UnitTest' matching the test ID '{testId}'."); @@ -238,7 +241,7 @@ private static void CollectEntriesAndErrors(string trxFile, XNamespace ns, List< continue; } - results.Add((testDefinitionClassName + "." + testResultTestName, testResultOutcome, testDefinitionStorage)); + results.Add(new(testDefinitionClassName + "." + testResultTestName, testResultOutcome, testDefinitionStorage)); } } } diff --git a/src/Platform/Microsoft.Testing.Extensions.TrxReport/TrxDataConsumer.cs b/src/Platform/Microsoft.Testing.Extensions.TrxReport/TrxDataConsumer.cs index 7999b00d47..7bca22a1ce 100644 --- a/src/Platform/Microsoft.Testing.Extensions.TrxReport/TrxDataConsumer.cs +++ b/src/Platform/Microsoft.Testing.Extensions.TrxReport/TrxDataConsumer.cs @@ -40,7 +40,7 @@ internal sealed class TrxReportGenerator : private readonly TrxTestApplicationLifecycleCallbacks? _trxTestApplicationLifecycleCallbacks; private readonly ILogger _logger; private readonly List _tests = []; - private readonly Dictionary> _artifactsByExtension = new(); + private readonly Dictionary> _artifactsByExtension = []; private readonly bool _isEnabled; private DateTimeOffset? _testStartTime; @@ -89,10 +89,10 @@ public TrxReportGenerator( public Type[] DataTypesProduced { get; } = [typeof(SessionFileArtifact)]; /// - public string Uid { get; } = nameof(TrxReportGenerator); + public string Uid => nameof(TrxReportGenerator); /// - public string Version { get; } = AppVersion.DefaultSemVer; + public string Version => AppVersion.DefaultSemVer; /// public string DisplayName { get; } = ExtensionResources.TrxReportGeneratorDisplayName; @@ -179,7 +179,7 @@ await _logger.LogDebugAsync($""" PlatformCommandLineProvider.ServerOption: {_commandLineOptionsService.IsOptionSet(PlatformCommandLineProvider.ServerOptionKey)} CrashDumpCommandLineOptions.CrashDumpOptionName: {_commandLineOptionsService.IsOptionSet(CrashDumpCommandLineOptions.CrashDumpOptionName)} TrxReportGeneratorCommandLine.IsTrxReportEnabled: {_commandLineOptionsService.IsOptionSet(TrxReportGeneratorCommandLine.TrxReportOptionName)} -"""); +""").ConfigureAwait(false); } if (!_commandLineOptionsService.IsOptionSet(PlatformCommandLineProvider.ServerOptionKey) && @@ -191,7 +191,7 @@ await _logger.LogDebugAsync($""" try { await _trxTestApplicationLifecycleCallbacks.NamedPipeClient.RequestReplyAsync(new TestAdapterInformationRequest(_testFramework.Uid, _testFramework.Version), cancellationToken) - .TimeoutAfterAsync(TimeoutHelper.DefaultHangTimeSpanTimeout, cancellationToken); + .TimeoutAfterAsync(TimeoutHelper.DefaultHangTimeSpanTimeout, cancellationToken).ConfigureAwait(false); } catch (OperationCanceledException ex) when (ex.CancellationToken == cancellationToken) { @@ -220,19 +220,19 @@ public async Task OnTestSessionFinishingAsync(SessionUid sessionUid, Cancellatio { if (!_adapterSupportTrxCapability) { - await _outputDisplay.DisplayAsync(this, new WarningMessageOutputDeviceData(string.Format(CultureInfo.InvariantCulture, ExtensionResources.TrxReportFrameworkDoesNotSupportTrxReportCapability, _testFramework.DisplayName, _testFramework.Uid))); + await _outputDisplay.DisplayAsync(this, new WarningMessageOutputDeviceData(string.Format(CultureInfo.InvariantCulture, ExtensionResources.TrxReportFrameworkDoesNotSupportTrxReportCapability, _testFramework.DisplayName, _testFramework.Uid))).ConfigureAwait(false); } ApplicationStateGuard.Ensure(_testStartTime is not null); int exitCode = _testApplicationProcessExitCode.GetProcessExitCode(); TrxReportEngine trxReportGeneratorEngine = new(_testApplicationModuleInfo, _environment, _commandLineOptionsService, _configuration, - _clock, _tests.ToArray(), _failedTestsCount, _passedTestsCount, _notExecutedTestsCount, _timeoutTestsCount, _artifactsByExtension, - _adapterSupportTrxCapability, _testFramework, _testStartTime.Value, exitCode, cancellationToken); - (string reportFileName, string? warning) = await trxReportGeneratorEngine.GenerateReportAsync(); + _clock, [.. _tests], _failedTestsCount, _passedTestsCount, _notExecutedTestsCount, _timeoutTestsCount, _artifactsByExtension, + _adapterSupportTrxCapability, _testFramework, _testStartTime.Value, exitCode, cancellationToken); + (string reportFileName, string? warning) = await trxReportGeneratorEngine.GenerateReportAsync().ConfigureAwait(false); if (warning is not null) { - await _outputDisplay.DisplayAsync(this, new WarningMessageOutputDeviceData(warning)); + await _outputDisplay.DisplayAsync(this, new WarningMessageOutputDeviceData(warning)).ConfigureAwait(false); } if ( @@ -242,13 +242,13 @@ public async Task OnTestSessionFinishingAsync(SessionUid sessionUid, Cancellatio !_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)); + await _messageBus.PublishAsync(this, new SessionFileArtifact(sessionUid, new FileInfo(reportFileName), ExtensionResources.TrxReportArtifactDisplayName, ExtensionResources.TrxReportArtifactDescription)).ConfigureAwait(false); } else { ApplicationStateGuard.Ensure(_trxTestApplicationLifecycleCallbacks is not null); ApplicationStateGuard.Ensure(_trxTestApplicationLifecycleCallbacks.NamedPipeClient is not null); - await _trxTestApplicationLifecycleCallbacks.NamedPipeClient.RequestReplyAsync(new ReportFileNameRequest(reportFileName), cancellationToken); + await _trxTestApplicationLifecycleCallbacks.NamedPipeClient.RequestReplyAsync(new ReportFileNameRequest(reportFileName), cancellationToken).ConfigureAwait(false); } } catch (OperationCanceledException ex) when (ex.CancellationToken == cancellationToken) diff --git a/src/Platform/Microsoft.Testing.Extensions.TrxReport/TrxProcessLifetimeHandler.cs b/src/Platform/Microsoft.Testing.Extensions.TrxReport/TrxProcessLifetimeHandler.cs index 903579a21a..ae3a26358a 100644 --- a/src/Platform/Microsoft.Testing.Extensions.TrxReport/TrxProcessLifetimeHandler.cs +++ b/src/Platform/Microsoft.Testing.Extensions.TrxReport/TrxProcessLifetimeHandler.cs @@ -44,7 +44,7 @@ internal sealed class TrxProcessLifetimeHandler : private readonly IOutputDevice _outputDevice; private readonly ILogger _logger; private readonly PipeNameDescription _pipeNameDescription; - private readonly Dictionary> _fileArtifacts = new(); + private readonly Dictionary> _fileArtifacts = []; private readonly DateTimeOffset _startTime; private NamedPipeServer? _singleConnectionNamedPipeServer; @@ -109,7 +109,7 @@ public Task BeforeTestHostProcessStartAsync(CancellationToken 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); + await _singleConnectionNamedPipeServer.WaitConnectionAsync(cancellation).TimeoutAfterAsync(TimeoutHelper.DefaultHangTimeSpanTimeout, cancellation).ConfigureAwait(false); }, cancellation); return Task.CompletedTask; @@ -122,7 +122,7 @@ public async Task OnTestHostProcessStartedAsync(ITestHostProcessInformation test throw new InvalidOperationException(ExtensionResources.TrxReportGeneratorBeforeTestHostProcessStartAsyncNotCalled); } - await _waitConnectionTask.TimeoutAfterAsync(TimeoutHelper.DefaultHangTimeSpanTimeout, cancellation); + await _waitConnectionTask.TimeoutAfterAsync(TimeoutHelper.DefaultHangTimeSpanTimeout, cancellation).ConfigureAwait(false); } public Task ConsumeAsync(IDataProducer dataProducer, IData value, CancellationToken cancellationToken) @@ -144,7 +144,7 @@ public async Task OnTestHostProcessExitedAsync(ITestHostProcessInformation testH return; } - Dictionary> artifacts = new(); + Dictionary> artifacts = []; foreach (KeyValuePair> prodArtifacts in _fileArtifacts) { @@ -172,10 +172,10 @@ public async Task OnTestHostProcessExitedAsync(ITestHostProcessInformation testH (string fileName, string? warning) = await trxReportGeneratorEngine.GenerateReportAsync( isTestHostCrashed: true, - testHostCrashInfo: $"Test host process pid: {testHostProcessInformation.PID} crashed."); + testHostCrashInfo: $"Test host process pid: {testHostProcessInformation.PID} crashed.").ConfigureAwait(false); if (warning is not null) { - await _outputDevice.DisplayAsync(this, new WarningMessageOutputDeviceData(warning)); + await _outputDevice.DisplayAsync(this, new WarningMessageOutputDeviceData(warning)).ConfigureAwait(false); } await _messageBus.PublishAsync( @@ -183,7 +183,7 @@ await _messageBus.PublishAsync( new FileArtifact( new FileInfo(fileName), ExtensionResources.TrxReportArtifactDisplayName, - ExtensionResources.TrxReportArtifactDescription)); + ExtensionResources.TrxReportArtifactDescription)).ConfigureAwait(false); return; } @@ -206,10 +206,10 @@ await _messageBus.PublishAsync( testHostProcessInformation.ExitCode, cancellation); - await trxReportGeneratorEngine.AddArtifactsAsync(trxFile, artifacts); + await trxReportGeneratorEngine.AddArtifactsAsync(trxFile, artifacts).ConfigureAwait(false); } - await _messageBus.PublishAsync(this, new FileArtifact(trxFile, ExtensionResources.TrxReportArtifactDisplayName, ExtensionResources.TrxReportArtifactDescription)); + await _messageBus.PublishAsync(this, new FileArtifact(trxFile, ExtensionResources.TrxReportArtifactDisplayName, ExtensionResources.TrxReportArtifactDescription)).ConfigureAwait(false); } private Task CallbackAsync(IRequest request) @@ -233,10 +233,10 @@ private Task CallbackAsync(IRequest request) #if NETCOREAPP public async ValueTask DisposeAsync() { - await DisposeHelper.DisposeAsync(_singleConnectionNamedPipeServer); + await DisposeHelper.DisposeAsync(_singleConnectionNamedPipeServer).ConfigureAwait(false); // Dispose the pipe descriptor after the server to ensure the pipe is closed. - _pipeNameDescription?.Dispose(); + _pipeNameDescription.Dispose(); } #else public void Dispose() @@ -244,7 +244,7 @@ public void Dispose() _singleConnectionNamedPipeServer?.Dispose(); // Dispose the pipe descriptor after the server to ensure the pipe is closed. - _pipeNameDescription?.Dispose(); + _pipeNameDescription.Dispose(); } #endif diff --git a/src/Platform/Microsoft.Testing.Extensions.TrxReport/TrxReportEngine.cs b/src/Platform/Microsoft.Testing.Extensions.TrxReport/TrxReportEngine.cs index e58b34329c..fbb1be1e21 100644 --- a/src/Platform/Microsoft.Testing.Extensions.TrxReport/TrxReportEngine.cs +++ b/src/Platform/Microsoft.Testing.Extensions.TrxReport/TrxReportEngine.cs @@ -148,7 +148,12 @@ public TrxReportEngine(IFileSystem fileSystem, ITestApplicationModuleInfo testAp // 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()); + if (!Guid.TryParse(_environment.GetEnvironmentVariable(EnvironmentVariableConstants.TESTINGPLATFORM_TRX_TESTRUN_ID), out Guid testRunId)) + { + testRunId = Guid.NewGuid(); + } + + testRun.SetAttributeValue("id", testRunId); string testRunName = $"{_environment.GetEnvironmentVariable("UserName")}@{_environment.MachineName} {FormatDateTimeForRunName(_clock.UtcNow)}"; testRun.SetAttributeValue("name", testRunName); @@ -177,7 +182,7 @@ public TrxReportEngine(IFileSystem fileSystem, ITestApplicationModuleInfo testAp // NotExecuted is the status for the skipped test. resultSummaryOutcome = isTestHostCrashed || _exitCode != ExitCodes.Success ? "Failed" : resultSummaryOutcome is "Passed" or "NotExecuted" ? "Completed" : resultSummaryOutcome; - await AddResultSummaryAsync(testRun, resultSummaryOutcome, runDeploymentRoot, testHostCrashInfo, _exitCode, isTestHostCrashed); + await AddResultSummaryAsync(testRun, resultSummaryOutcome, runDeploymentRoot, testHostCrashInfo, _exitCode, isTestHostCrashed).ConfigureAwait(false); // will need catch Unauthorized access document.Add(testRun); @@ -198,16 +203,16 @@ public TrxReportEngine(IFileSystem fileSystem, ITestApplicationModuleInfo testAp string outputDirectory = _configuration.GetTestResultDirectory(); // add var for this string finalFileName = Path.Combine(outputDirectory, trxFileName); - bool isFileNameExplicitlyProvidedAndFileExists = isFileNameExplicitlyProvided && _fileSystem.Exists(finalFileName); + bool isFileNameExplicitlyProvidedAndFileExists = isFileNameExplicitlyProvided && _fileSystem.ExistFile(finalFileName); // Note that we need to dispose the IFileStream, not the inner stream. // IFileStream implementations will be responsible to dispose their inner stream. using IFileStream stream = _fileSystem.NewFileStream(finalFileName, isFileNameExplicitlyProvided ? FileMode.Create : FileMode.CreateNew); - await document.SaveAsync(stream.Stream, SaveOptions.None, _cancellationToken); + await document.SaveAsync(stream.Stream, SaveOptions.None, _cancellationToken).ConfigureAwait(false); return isFileNameExplicitlyProvidedAndFileExists ? (finalFileName, string.Format(CultureInfo.InvariantCulture, ExtensionResources.TrxFileExistsAndWillBeOverwritten, finalFileName)) : (finalFileName, null); - }); + }).ConfigureAwait(false); private async Task<(string FileName, string? Warning)> RetryWhenIOExceptionAsync(Func> func) { @@ -217,7 +222,7 @@ public TrxReportEngine(IFileSystem fileSystem, ITestApplicationModuleInfo testAp { try { - return await func(); + return await func().ConfigureAwait(false); } catch (IOException) { @@ -254,10 +259,10 @@ public async Task AddArtifactsAsync(FileInfo trxFile, Dictionary> artifacts, XElement collectorDataEntries, string runDeploymentRoot) @@ -276,7 +281,7 @@ private async Task AddArtifactsToCollectionAsync(Dictionary CopyArtifactIntoTrxDirectoryAndReturnHrefValueAsync(FileInfo artifact, string runDeploymentRoot) @@ -368,7 +373,7 @@ private async Task CopyArtifactIntoTrxDirectoryAndReturnHrefValueAsync(F break; } - await CopyFileAsync(artifact, new FileInfo(destination)); + await CopyFileAsync(artifact, new FileInfo(destination)).ConfigureAwait(false); return Path.Combine(_environment.MachineName, Path.GetFileName(destination)); } @@ -393,7 +398,7 @@ private async Task CopyFileAsync(FileInfo origin, FileInfo destination) using FileStream fileStream = File.OpenRead(origin.FullName); using var destinationStream = new FileStream(destination.FullName, FileMode.Create); - await fileStream.CopyToAsync(destinationStream, _cancellationToken); + await fileStream.CopyToAsync(destinationStream, _cancellationToken).ConfigureAwait(false); } private static void AddTestLists(XElement testRun, string uncategorizedTestId) @@ -518,8 +523,7 @@ private void AddResults(string testAppModule, XElement testRun, out XElement tes output.Add(errorInfoElement); } - // add collectorDataEntries details - if (output.HasElements && outcome != "NotExecuted") + if (output.HasElements) { unitTestResult.Add(output); } @@ -555,6 +559,76 @@ private void AddResults(string testAppModule, XElement testRun, out XElement tes unitTest.Add(new XElement("Execution", new XAttribute("id", executionId))); + XElement? properties = null; + XElement? owners = null; + XElement? description = null; + foreach (TestMetadataProperty property in testNode.Properties.OfType()) + { + switch (property.Key) + { + case "Owner": + owners ??= new XElement("Owners", new XElement("Owner", new XAttribute("name", property.Value))); + break; + + case "Priority": + if (int.TryParse(property.Value, out _)) + { + unitTest.SetAttributeValue("priority", property.Value); + } + + break; + + case "Description": + description ??= new XElement("Description", property.Value); + break; + + default: + // NOTE: VSTest doesn't produce Properties as of writing this. + // It was historically fixed, but the fix wasn't correct and the fix was reverted and never revisited to be properly fixed. + // Revert PR: https://github.com/microsoft/vstest/pull/15080 + // The original implementation (buggy) was setting "Key" and "Value" as attributes on "Property" element. + // However, Visual Studio will validate the TRX file against vstst.xsd file in + // C:\Program Files\Microsoft Visual Studio\2022\Enterprise\Xml\Schemas\vstst.xsd + // In xsd, "Properties" element is defined as: + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // So, Key and Value are **elements**, not attributes. + // In MTP, we do the right thing and follow the XSD definition. + properties ??= new XElement("Properties"); + properties.Add(new XElement( + "Property", + new XElement("Key", property.Key), new XElement("Value", property.Value))); + break; + } + } + + if (owners is not null) + { + unitTest.Add(owners); + } + + if (description is not null) + { + unitTest.Add(description); + } + + if (properties is not null) + { + unitTest.Add(properties); + } + var testMethod = new XElement( "TestMethod", new XAttribute("codeBase", testAppModule), @@ -619,7 +693,7 @@ 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); + date.ToString("yyyy-MM-dd HH:mm:ss.fffffff", DateTimeFormatInfo.InvariantInfo); private static string ReplaceInvalidFileNameChars(string fileName) { @@ -653,7 +727,7 @@ private static bool IsReservedFileName(string fileName) => // 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; + ReservedFileNamesRegex.IsMatch(fileName); private static Guid GuidFromString(string data) { @@ -672,8 +746,7 @@ private static Guid GuidFromString(string data) ArrayPool.Shared.Return(dataBytes); } #else - var sha256 = SHA256.Create(); - byte[] hash = sha256.ComputeHash(Encoding.Unicode.GetBytes(data)); + byte[] hash = SHA256.HashData(Encoding.Unicode.GetBytes(data)); byte[] bytes = new byte[16]; Array.Copy(hash, bytes, 16); return new Guid(bytes); diff --git a/src/Platform/Microsoft.Testing.Extensions.TrxReport/TrxReportExtensions.cs b/src/Platform/Microsoft.Testing.Extensions.TrxReport/TrxReportExtensions.cs index eb769731a0..7962b07e30 100644 --- a/src/Platform/Microsoft.Testing.Extensions.TrxReport/TrxReportExtensions.cs +++ b/src/Platform/Microsoft.Testing.Extensions.TrxReport/TrxReportExtensions.cs @@ -47,7 +47,7 @@ public static void AddTrxReportProvider(this ITestApplicationBuilder builder) serviceProvider.GetService(), serviceProvider.GetLoggerFactory().CreateLogger())); - builder.TestHost.AddTestApplicationLifecycleCallbacks(serviceProvider => + builder.TestHost.AddTestHostApplicationLifetime(serviceProvider => new TrxTestApplicationLifecycleCallbacks( serviceProvider.GetCommandLineOptions(), serviceProvider.GetEnvironment())); @@ -56,7 +56,7 @@ public static void AddTrxReportProvider(this ITestApplicationBuilder builder) builder.CommandLine.AddProvider(() => commandLine); - PipeNameDescription pipeNameDescription = NamedPipeServer.GetPipeName(Guid.NewGuid().ToString("N")); + PipeNameDescription pipeNameDescription = NamedPipeServer.GetPipeName(Guid.NewGuid().ToString("N"), new SystemEnvironment()); var compositeLifeTimeHandler = new CompositeExtensionFactory(serviceProvider => { diff --git a/src/Platform/Microsoft.Testing.Extensions.TrxReport/TrxTestApplicationLifecycleCallbacks.cs b/src/Platform/Microsoft.Testing.Extensions.TrxReport/TrxTestApplicationLifecycleCallbacks.cs index 33e0e73cff..0fc71314fa 100644 --- a/src/Platform/Microsoft.Testing.Extensions.TrxReport/TrxTestApplicationLifecycleCallbacks.cs +++ b/src/Platform/Microsoft.Testing.Extensions.TrxReport/TrxTestApplicationLifecycleCallbacks.cs @@ -12,7 +12,7 @@ namespace Microsoft.Testing.Extensions.TrxReport.Abstractions; -internal sealed class TrxTestApplicationLifecycleCallbacks : ITestApplicationLifecycleCallbacks, IDisposable +internal sealed class TrxTestApplicationLifecycleCallbacks : ITestHostApplicationLifetime, IDisposable { private readonly bool _isEnabled; private readonly IEnvironment _environment; @@ -34,10 +34,10 @@ public TrxTestApplicationLifecycleCallbacks( public NamedPipeClient? NamedPipeClient { get; private set; } - public string Uid { get; } = nameof(TrxTestApplicationLifecycleCallbacks); + public string Uid => nameof(TrxTestApplicationLifecycleCallbacks); /// - public string Version { get; } = AppVersion.DefaultSemVer; + public string Version => AppVersion.DefaultSemVer; /// public string DisplayName { get; } = ExtensionResources.TrxReportGeneratorDisplayName; @@ -63,13 +63,13 @@ public async Task BeforeRunAsync(CancellationToken cancellationToken) { string namedPipeName = _environment.GetEnvironmentVariable(TrxEnvironmentVariableProvider.TRXNAMEDPIPENAME) ?? throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, ExtensionResources.TrxReportGeneratorMissingTrxNamedPipeEnvironmentVariable, TrxEnvironmentVariableProvider.TRXNAMEDPIPENAME)); - NamedPipeClient = new NamedPipeClient(namedPipeName); + NamedPipeClient = new NamedPipeClient(namedPipeName, _environment); 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, cancellationToken); + await NamedPipeClient.ConnectAsync(cancellationToken).TimeoutAfterAsync(TimeoutHelper.DefaultHangTimeSpanTimeout, cancellationToken).ConfigureAwait(false); } } catch (OperationCanceledException ex) when (ex.CancellationToken == cancellationToken) diff --git a/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/BannedSymbols.txt b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/BannedSymbols.txt index 13daece7e1..6c52641180 100644 --- a/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/BannedSymbols.txt +++ b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/BannedSymbols.txt @@ -1,5 +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); 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/VSTestBridgeExtensionBaseCapabilities.cs b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/Capabilities/VSTestBridgeExtensionBaseCapabilities.cs index 4c17f77738..23558ea6ea 100644 --- a/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/Capabilities/VSTestBridgeExtensionBaseCapabilities.cs +++ b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/Capabilities/VSTestBridgeExtensionBaseCapabilities.cs @@ -18,7 +18,7 @@ public sealed class VSTestBridgeExtensionBaseCapabilities : ITrxReportCapability private const string VSTestProviderSupport = "vstestProvider"; /// - bool ITrxReportCapability.IsSupported { get; } = true; + bool ITrxReportCapability.IsSupported => true; /// /// Gets a value indicating whether a flag indicating whether the trx report capability is enabled. diff --git a/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/CommandLine/RunSettingsCommandLineOptionsProvider.cs b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/CommandLine/RunSettingsCommandLineOptionsProvider.cs index 261f1e58d5..8affc8e609 100644 --- a/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/CommandLine/RunSettingsCommandLineOptionsProvider.cs +++ b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/CommandLine/RunSettingsCommandLineOptionsProvider.cs @@ -57,7 +57,7 @@ public Task ValidateOptionArgumentsAsync(CommandLineOption com RoslynDebug.Assert(commandOption.Name == RunSettingsOptionName); string filePath = arguments[0]; - if (!_fileSystem.Exists(filePath)) + if (!_fileSystem.ExistFile(filePath)) { return ValidationResult.InvalidTask(string.Format(CultureInfo.InvariantCulture, ExtensionResources.RunsettingsFileDoesNotExist, filePath)); } diff --git a/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/Configurations/RunSettingsConfigurationProvider.cs b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/Configurations/RunSettingsConfigurationProvider.cs index da7ff8d85c..9651a8ce33 100644 --- a/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/Configurations/RunSettingsConfigurationProvider.cs +++ b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/Configurations/RunSettingsConfigurationProvider.cs @@ -1,7 +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.Extensions.VSTestBridge.CommandLine; +using Microsoft.Testing.Extensions.VSTestBridge.Helpers; +using Microsoft.Testing.Platform; using Microsoft.Testing.Platform.CommandLine; using Microsoft.Testing.Platform.Configurations; using Microsoft.Testing.Platform.Helpers; @@ -15,16 +16,16 @@ internal sealed class RunSettingsConfigurationProvider(IFileSystem fileSystem) : private string? _runSettingsFileContent; /// - public string Uid { get; } = nameof(RunSettingsConfigurationProvider); + public string Uid => nameof(RunSettingsConfigurationProvider); /// - public string Version { get; } = AppVersion.DefaultSemVer; + public string Version => AppVersion.DefaultSemVer; /// - public string DisplayName { get; } = "VSTest Helpers: runsettings configuration"; + public string DisplayName => "VSTest Helpers: runsettings configuration"; /// - public string Description { get; } = "Configuration source to bridge VSTest xml runsettings configuration into Microsoft Testing Platform configuration model."; + public string Description => "Configuration source to bridge VSTest xml runsettings configuration into Microsoft Testing Platform configuration model."; public int Order => 2; @@ -37,7 +38,7 @@ internal sealed class RunSettingsConfigurationProvider(IFileSystem fileSystem) : /// public bool TryGet(string key, out string? value) { - if (_runSettingsFileContent is null) + if (RoslynString.IsNullOrEmpty(_runSettingsFileContent)) { value = null; return false; @@ -58,16 +59,9 @@ public bool TryGet(string key, out string? value) } /// - public async Task BuildAsync(CommandLineParseResult commandLineParseResult) + public Task BuildAsync(CommandLineParseResult commandLineParseResult) { - if (commandLineParseResult.TryGetOptionArgumentList(RunSettingsCommandLineOptionsProvider.RunSettingsOptionName, out string[]? runSettingsFilePath)) - { - if (_fileSystem.Exists(runSettingsFilePath[0])) - { - _runSettingsFileContent = await _fileSystem.ReadAllTextAsync(runSettingsFilePath[0]); - } - } - - return this; + _runSettingsFileContent = RunSettingsHelpers.ReadRunSettings(commandLineParseResult, _fileSystem); + return Task.FromResult(this); } } diff --git a/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/Helpers/RunSettingsHelpers.cs b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/Helpers/RunSettingsHelpers.cs new file mode 100644 index 0000000000..b46a062d81 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/Helpers/RunSettingsHelpers.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.Extensions.VSTestBridge.CommandLine; +using Microsoft.Testing.Platform; +using Microsoft.Testing.Platform.CommandLine; +using Microsoft.Testing.Platform.Helpers; + +namespace Microsoft.Testing.Extensions.VSTestBridge.Helpers; + +internal static class RunSettingsHelpers +{ + // TODO: There are two code paths that read runsettings. One is calling the ICommandLineOptions overload, other is calling the CommandLineParseResult overload. + // Figure out if they can/should be unified so that we do one I/O operation instead of two. + public static string ReadRunSettings(ICommandLineOptions commandLineOptions, IFileSystem fileSystem) + { + _ = commandLineOptions.TryGetOptionArgumentList(RunSettingsCommandLineOptionsProvider.RunSettingsOptionName, out string[]? fileNames); + return ReadRunSettings(fileNames, fileSystem); + } + + public static string ReadRunSettings(CommandLineParseResult commandLineParseResult, IFileSystem fileSystem) + { + _ = commandLineParseResult.TryGetOptionArgumentList(RunSettingsCommandLineOptionsProvider.RunSettingsOptionName, out string[]? fileNames); + return ReadRunSettings(fileNames, fileSystem); + } + + private static string ReadRunSettings(string[]? runsettingsFileFromCommandLine, IFileSystem fileSystem) + { + if (runsettingsFileFromCommandLine is not null && + runsettingsFileFromCommandLine.Length == 1 && + fileSystem.ExistFile(runsettingsFileFromCommandLine[0])) + { + return fileSystem.ReadAllText(runsettingsFileFromCommandLine[0]); + } + else + { + string? envVariableRunSettings = Environment.GetEnvironmentVariable("TESTINGPLATFORM_EXPERIMENTAL_VSTEST_RUNSETTINGS"); + if (!RoslynString.IsNullOrEmpty(envVariableRunSettings)) + { + return envVariableRunSettings; + } + else + { + string? runSettingsFilePath = Environment.GetEnvironmentVariable("TESTINGPLATFORM_VSTESTBRIDGE_RUNSETTINGS_FILE"); + + if (!RoslynString.IsNullOrEmpty(runSettingsFilePath) && File.Exists(runSettingsFilePath)) + { + return fileSystem.ReadAllText(runSettingsFilePath); + } + } + } + + return string.Empty; + } +} 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 index fd51a85275..cf8668354d 100644 --- a/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/Microsoft.Testing.Extensions.VSTestBridge.csproj +++ b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/Microsoft.Testing.Extensions.VSTestBridge.csproj @@ -36,10 +36,6 @@ This package provides a bridge integration for test adapters wanting to target b - - - - diff --git a/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/ObjectModel/Condition.cs b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/ObjectModel/Condition.cs index 90d9101af0..9c072cdb0f 100644 --- a/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/ObjectModel/Condition.cs +++ b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/ObjectModel/Condition.cs @@ -164,7 +164,7 @@ internal static Condition Parse(string? conditionString) ThrownFormatExceptionForInvalidCondition(conditionString); } - string[] parts = TokenizeFilterConditionString(conditionString).ToArray(); + string[] parts = [.. TokenizeFilterConditionString(conditionString)]; if (parts.Length == 1) { // If only parameter values is passed, create condition with default property name, @@ -193,9 +193,9 @@ internal static Condition Parse(string? conditionString) } [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))); + private static void ThrownFormatExceptionForInvalidCondition(string? conditionString) => + throw new FormatException( + $"Incorrect format for TestCaseFilter Error: Invalid Condition '{conditionString}'. Specify the correct format and try again. Note that the incorrect format can lead to no test getting executed.."); /// /// Check if condition validates any property in properties. @@ -247,8 +247,7 @@ private bool ValidForContainsOperation(Func? propertyProv "~" => 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))), + $"Incorrect format for TestCaseFilter Error: Invalid operator '{operationString}'. Specify the correct format and try again. Note that the incorrect format can lead to no test getting executed.."), }; /// diff --git a/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/ObjectModel/ContextAdapterBase.cs b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/ObjectModel/ContextAdapterBase.cs index 7121115b98..49579940eb 100644 --- a/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/ObjectModel/ContextAdapterBase.cs +++ b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/ObjectModel/ContextAdapterBase.cs @@ -61,14 +61,9 @@ protected ContextAdapterBase(ICommandLineOptions commandLineOptions, IRunSetting string validPropertiesString = supportedProperties == null ? string.Empty : string.Join(", ", supportedProperties); - 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); + EqtTrace.Info($"No tests matched the filter because it contains one or more properties that are not valid ({string.Join(", ", invalidProperties)}). Specify filter expression containing valid properties ({validPropertiesString})."); } return adapterSpecificTestCaseFilter; diff --git a/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/ObjectModel/FastFilter.cs b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/ObjectModel/FastFilter.cs index 456384b72c..1a8c3ebdb1 100644 --- a/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/ObjectModel/FastFilter.cs +++ b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/ObjectModel/FastFilter.cs @@ -21,7 +21,7 @@ internal FastFilter(ImmutableDictionary> filterProperties, (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."))); + : throw new ArgumentException("An error occurred while creating Fast filter.")); } internal ImmutableDictionary> FilterProperties { get; } @@ -37,7 +37,7 @@ internal FastFilter(ImmutableDictionary> filterProperties, ? null : FilterProperties.Keys.All(name => properties.Contains(name)) ? null - : FilterProperties.Keys.Where(name => !properties.Contains(name)).ToArray(); + : [.. FilterProperties.Keys.Where(name => !properties.Contains(name))]; internal bool Evaluate(Func propertyValueProvider) { diff --git a/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/ObjectModel/FilterExpression.cs b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/ObjectModel/FilterExpression.cs index 4f771bfc0f..be6f23da2b 100644 --- a/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/ObjectModel/FilterExpression.cs +++ b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/ObjectModel/FilterExpression.cs @@ -136,7 +136,7 @@ private static void ProcessOperator(Stack filterStack, Operato } else if (invalidRight != null) { - invalidProperties = invalidProperties.Concat(invalidRight).ToArray(); + invalidProperties = [.. invalidProperties, .. invalidRight]; } return invalidProperties; diff --git a/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/ObjectModel/FrameworkHandlerAdapter.cs b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/ObjectModel/FrameworkHandlerAdapter.cs index 865ba66023..c0c58665a8 100644 --- a/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/ObjectModel/FrameworkHandlerAdapter.cs +++ b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/ObjectModel/FrameworkHandlerAdapter.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. -#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. - using Microsoft.Testing.Extensions.VSTestBridge.Helpers; using Microsoft.Testing.Platform.Capabilities.TestFramework; using Microsoft.Testing.Platform.CommandLine; @@ -140,7 +138,7 @@ public void RecordResult(TestResult testResult) _frameworkHandle?.RecordResult(testResult); // Publish node state change to Microsoft Testing Platform - var testNode = testResult.ToTestNode(_isTrxEnabled, _namedFeatureCapability, _commandLineOptions, _clientInfo); + var testNode = testResult.ToTestNode(_isTrxEnabled, _adapterExtensionBase.UseFullyQualifiedNameAsTestNodeUid, _namedFeatureCapability, _commandLineOptions, _clientInfo); var testNodeChange = new TestNodeUpdateMessage(_session.SessionUid, testNode); _messageBus.PublishAsync(_adapterExtensionBase, testNodeChange).Await(); @@ -159,7 +157,7 @@ public void RecordStart(TestCase testCase) _frameworkHandle?.RecordStart(testCase); // Publish node state change to Microsoft Testing Platform - var testNode = testCase.ToTestNode(_isTrxEnabled, _namedFeatureCapability, _commandLineOptions, _clientInfo); + var testNode = testCase.ToTestNode(_isTrxEnabled, _adapterExtensionBase.UseFullyQualifiedNameAsTestNodeUid, _namedFeatureCapability, _commandLineOptions, _clientInfo); testNode.Properties.Add(InProgressTestNodeStateProperty.CachedInstance); var testNodeChange = new TestNodeUpdateMessage(_session.SessionUid, testNode); @@ -182,7 +180,7 @@ private async Task PublishTestSessionAttachmentsAsync(IEnumerable } var fileArtifact = new SessionFileArtifact(_session.SessionUid, new(attachment.Uri.LocalPath), attachmentSet.DisplayName, attachment.Description); - await _messageBus.PublishAsync(_adapterExtensionBase, fileArtifact); + await _messageBus.PublishAsync(_adapterExtensionBase, fileArtifact).ConfigureAwait(false); } } } diff --git a/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/ObjectModel/ObjectModelConverters.cs b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/ObjectModel/ObjectModelConverters.cs index 4a801d1c4d..66ea0696b2 100644 --- a/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/ObjectModel/ObjectModelConverters.cs +++ b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/ObjectModel/ObjectModelConverters.cs @@ -34,24 +34,21 @@ internal static class ObjectModelConverters valueType: typeof(string), owner: typeof(TestCase)); - private static readonly TestProperty TestCategoryProperty = TestProperty.Register( - id: "MSTestDiscoverer.TestCategory", - label: "TestCategory", - valueType: typeof(string[]), - owner: typeof(TestCase)); - - private static readonly TestProperty TraitsProperty = TestProperty.Register( - id: "TestObject.Traits", - label: "Traits", - valueType: typeof(KeyValuePair[]), - owner: typeof(TestObject)); + private static readonly Uri ExecutorUri = new(Constants.ExecutorUri); /// /// Converts a VSTest to a Microsoft Testing Platform . /// - public static TestNode ToTestNode(this TestCase testCase, bool isTrxEnabled, INamedFeatureCapability? namedFeatureCapability, ICommandLineOptions commandLineOptions, IClientInfo clientInfo, string? displayNameFromTestResult = null) + public static TestNode ToTestNode( + this TestCase testCase, + bool isTrxEnabled, + bool useFullyQualifiedNameAsUid, + INamedFeatureCapability? namedFeatureCapability, + ICommandLineOptions commandLineOptions, + IClientInfo clientInfo, + string? displayNameFromTestResult = null) { - string testNodeUid = testCase.Id.ToString(); + string testNodeUid = useFullyQualifiedNameAsUid ? testCase.FullyQualifiedName : testCase.Id.ToString(); TestNode testNode = new() { @@ -82,27 +79,33 @@ public static TestNode ToTestNode(this TestCase testCase, bool isTrxEnabled, INa private static void CopyCategoryAndTraits(TestObject testCaseOrResult, TestNode testNode, bool 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 (testCaseOrResult.GetPropertyValue(TestCategoryProperty, defaultValue: null) is string[] mstestCategories) + foreach (KeyValuePair property in testCaseOrResult.GetProperties()) { - if (isTrxEnabled) +#pragma warning disable CS0618 // Type or member is obsolete + if ((property.Key.Attributes & TestPropertyAttributes.Trait) == 0) +#pragma warning restore CS0618 // Type or member is obsolete { - testNode.Properties.Add(new TrxCategoriesProperty(mstestCategories)); + continue; } - foreach (string category in mstestCategories) + if (property.Value is string[] categories) { - testNode.Properties.Add(new TestMetadataProperty(category, string.Empty)); - } - } + if (isTrxEnabled) + { + testNode.Properties.Add(new TrxCategoriesProperty(categories)); + } - if (testCaseOrResult.GetPropertyValue[]>(TraitsProperty, defaultValue: null) is KeyValuePair[] traits && - traits.Length > 0) - { - foreach (KeyValuePair trait in traits) + foreach (string category in categories) + { + testNode.Properties.Add(new TestMetadataProperty(category, string.Empty)); + } + } + else if (property.Value is KeyValuePair[] traits) { - testNode.Properties.Add(new TestMetadataProperty(trait.Key, trait.Value)); + foreach (KeyValuePair trait in traits) + { + testNode.Properties.Add(new TestMetadataProperty(trait.Key, trait.Value)); + } } } } @@ -135,9 +138,15 @@ private static void CopyVSTestProviderProperties(TestNode testNode, TestCase tes /// /// Converts a VSTest to a Microsoft Testing Platform . /// - public static TestNode ToTestNode(this TestResult testResult, bool isTrxEnabled, INamedFeatureCapability? namedFeatureCapability, ICommandLineOptions commandLineOptions, IClientInfo clientInfo) + public static TestNode ToTestNode( + this TestResult testResult, + bool isTrxEnabled, + bool useFullyQualifiedNameAsUid, + INamedFeatureCapability? namedFeatureCapability, + ICommandLineOptions commandLineOptions, + IClientInfo clientInfo) { - var testNode = testResult.TestCase.ToTestNode(isTrxEnabled, namedFeatureCapability, commandLineOptions, clientInfo, testResult.DisplayName); + var testNode = testResult.TestCase.ToTestNode(isTrxEnabled, useFullyQualifiedNameAsUid, namedFeatureCapability, commandLineOptions, clientInfo, testResult.DisplayName); CopyCategoryAndTraits(testResult, testNode, isTrxEnabled); @@ -145,7 +154,10 @@ public static TestNode ToTestNode(this TestResult testResult, bool isTrxEnabled, if (isTrxEnabled) { - testNode.Properties.Add(new TrxExceptionProperty(testResult.ErrorMessage, testResult.ErrorStackTrace)); + if (!RoslynString.IsNullOrEmpty(testResult.ErrorMessage) || !RoslynString.IsNullOrEmpty(testResult.ErrorStackTrace)) + { + testNode.Properties.Add(new TrxExceptionProperty(testResult.ErrorMessage, testResult.ErrorStackTrace)); + } if (TryParseFullyQualifiedType(testResult.TestCase.FullyQualifiedName, out string? fullyQualifiedType)) { @@ -156,7 +168,7 @@ public static TestNode ToTestNode(this TestResult testResult, bool isTrxEnabled, throw new InvalidOperationException("Unable to parse fully qualified type name from test case: " + testResult.TestCase.FullyQualifiedName); } - testNode.Properties.Add(new TrxMessagesProperty(testResult.Messages + testNode.Properties.Add(new TrxMessagesProperty([.. testResult.Messages .Select(msg => msg.Category switch { @@ -164,14 +176,13 @@ public static TestNode ToTestNode(this TestResult testResult, bool isTrxEnabled, 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), [])); - var standardErrorMessages = new List(); - var standardOutputMessages = new List(); + List? standardErrorMessages = null; + List? standardOutputMessages = null; bool addVSTestProviderProperties = ShouldAddVSTestProviderProperties(namedFeatureCapability, commandLineOptions); foreach (TestResultMessage testResultMessage in testResult.Messages) { @@ -183,7 +194,7 @@ public static TestNode ToTestNode(this TestResult testResult, bool isTrxEnabled, testNode.Properties.Add(new SerializableKeyValuePairStringProperty("vstest.TestCase.StandardError", message)); } - standardErrorMessages.Add(message); + (standardErrorMessages ??= []).Add(message); } if (testResultMessage.Category == TestResultMessage.StandardOutCategory) @@ -194,7 +205,7 @@ public static TestNode ToTestNode(this TestResult testResult, bool isTrxEnabled, testNode.Properties.Add(new SerializableKeyValuePairStringProperty("vstest.TestCase.StandardOutput", message)); } - standardOutputMessages.Add(message); + (standardOutputMessages ??= []).Add(message); } } @@ -206,12 +217,12 @@ public static TestNode ToTestNode(this TestResult testResult, bool isTrxEnabled, } } - if (standardErrorMessages.Count > 0) + if (standardErrorMessages is { Count: > 0 }) { testNode.Properties.Add(new StandardErrorProperty(string.Join(Environment.NewLine, standardErrorMessages))); } - if (standardOutputMessages.Count > 0) + if (standardOutputMessages is { Count: > 0 }) { testNode.Properties.Add(new StandardOutputProperty(string.Join(Environment.NewLine, standardOutputMessages))); } @@ -264,7 +275,7 @@ internal static void FixUpTestCase(this TestCase testCase, string? testAssemblyP testCase.SetPropertyValue(OriginalExecutorUriProperty, testCase.ExecutorUri); } - testCase.ExecutorUri = new(Constants.ExecutorUri); + testCase.ExecutorUri = ExecutorUri; } private static bool TryGetMethodIdentifierProperty(TestCase testCase, [NotNullWhen(true)] out TestMethodIdentifierProperty? methodIdentifierProperty) diff --git a/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/ObjectModel/RunSettingsAdapter.cs b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/ObjectModel/RunSettingsAdapter.cs index 912eaa084a..06cc0ce7a2 100644 --- a/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/ObjectModel/RunSettingsAdapter.cs +++ b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/ObjectModel/RunSettingsAdapter.cs @@ -1,11 +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. -#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. - -using Microsoft.Testing.Extensions.VSTestBridge.CommandLine; +using Microsoft.Testing.Extensions.VSTestBridge.Helpers; using Microsoft.Testing.Extensions.VSTestBridge.Resources; -using Microsoft.Testing.Platform; using Microsoft.Testing.Platform.CommandLine; using Microsoft.Testing.Platform.Configurations; using Microsoft.Testing.Platform.Helpers; @@ -40,31 +37,7 @@ public RunSettingsAdapter( ILoggerFactory loggerFactory, IMessageLogger messageLogger) { - string? runSettingsXml = string.Empty; - - if (commandLineOptions.TryGetOptionArgumentList(RunSettingsCommandLineOptionsProvider.RunSettingsOptionName, out string[]? fileNames) - && fileNames is not null - && fileNames.Length == 1 - && fileSystem.Exists(fileNames[0])) - { - runSettingsXml = fileSystem.ReadAllText(fileNames[0]); - } - else - { - if (!RoslynString.IsNullOrEmpty(Environment.GetEnvironmentVariable("TESTINGPLATFORM_EXPERIMENTAL_VSTEST_RUNSETTINGS"))) - { - runSettingsXml = Environment.GetEnvironmentVariable("TESTINGPLATFORM_EXPERIMENTAL_VSTEST_RUNSETTINGS"); - } - else - { - string? runSettingsFilePath = Environment.GetEnvironmentVariable("TESTINGPLATFORM_VSTESTBRIDGE_RUNSETTINGS_FILE"); - - if (!RoslynString.IsNullOrEmpty(runSettingsFilePath) && File.Exists(runSettingsFilePath)) - { - runSettingsXml = fileSystem.ReadAllText(runSettingsFilePath); - } - } - } + string runSettingsXml = RunSettingsHelpers.ReadRunSettings(commandLineOptions, fileSystem); XDocument runSettingsDocument = RunSettingsPatcher.Patch(runSettingsXml, configuration, client, commandLineOptions); WarnOnUnsupportedEntries(runSettingsDocument, messageLogger); diff --git a/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/ObjectModel/RunSettingsPatcher.cs b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/ObjectModel/RunSettingsPatcher.cs index 85cc81e2e2..7a7dceecd5 100644 --- a/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/ObjectModel/RunSettingsPatcher.cs +++ b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/ObjectModel/RunSettingsPatcher.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. -#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. - using Microsoft.Testing.Extensions.VSTestBridge.CommandLine; using Microsoft.Testing.Extensions.VSTestBridge.Resources; using Microsoft.Testing.Platform; @@ -49,12 +47,6 @@ private static XDocument PatchSettingsWithDefaults(string? runSettingsXml, bool 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); @@ -107,7 +99,7 @@ private static void PatchTestRunParameters(XDocument runSettingsDocument, IComma runSettingsElement.Add(testRunParametersElement); } - XElement[] testRunParametersNodes = testRunParametersElement.Nodes().OfType().ToArray(); + XElement[] testRunParametersNodes = [.. testRunParametersElement.Nodes().OfType()]; foreach (string testRunParameter in testRunParameters) { string[] parts = testRunParameter.Split(TestRunParameterSeparator, 2); diff --git a/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/ObjectModel/TestCaseDiscoverySinkAdapter.cs b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/ObjectModel/TestCaseDiscoverySinkAdapter.cs index 2fda4f9620..f3d7226158 100644 --- a/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/ObjectModel/TestCaseDiscoverySinkAdapter.cs +++ b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/ObjectModel/TestCaseDiscoverySinkAdapter.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. -#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. - using Microsoft.Testing.Extensions.VSTestBridge.Helpers; using Microsoft.Testing.Platform.Capabilities.TestFramework; using Microsoft.Testing.Platform.CommandLine; @@ -94,7 +92,7 @@ public void SendTestCase(TestCase discoveredTest) _testCaseDiscoverySink?.SendTestCase(discoveredTest); // Publish node state change to Microsoft Testing Platform - var testNode = discoveredTest.ToTestNode(_isTrxEnabled, _namedFeatureCapability, _commandLineOptions, _clientInfo); + var testNode = discoveredTest.ToTestNode(_isTrxEnabled, _adapterExtension.UseFullyQualifiedNameAsTestNodeUid, _namedFeatureCapability, _commandLineOptions, _clientInfo); testNode.Properties.Add(DiscoveredTestNodeStateProperty.CachedInstance); var testNodeChange = new TestNodeUpdateMessage(_session.SessionUid, testNode); diff --git a/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/PublicAPI.Shipped.txt b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/PublicAPI.Shipped.txt index 16d6e94640..8af37ceb0c 100644 --- a/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/PublicAPI.Shipped.txt +++ b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/PublicAPI.Shipped.txt @@ -57,3 +57,4 @@ static Microsoft.Testing.Extensions.VSTestBridge.Helpers.TestApplicationBuilderE 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 +virtual Microsoft.Testing.Extensions.VSTestBridge.VSTestBridgedTestFrameworkBase.UseFullyQualifiedNameAsTestNodeUid.get -> bool diff --git a/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/SynchronizedSingleSessionVSTestAndTestAnywhereAdapter.cs b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/SynchronizedSingleSessionVSTestAndTestAnywhereAdapter.cs index 57ae617299..0789698909 100644 --- a/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/SynchronizedSingleSessionVSTestAndTestAnywhereAdapter.cs +++ b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/SynchronizedSingleSessionVSTestAndTestAnywhereAdapter.cs @@ -52,7 +52,7 @@ protected SynchronizedSingleSessionVSTestBridgedTestFramework(IExtension extensi public sealed override string Version => _extension.Version; /// - public override async Task IsEnabledAsync() => await _extension.IsEnabledAsync(); + public override async Task IsEnabledAsync() => await _extension.IsEnabledAsync().ConfigureAwait(false); /// public sealed override Task CreateTestSessionAsync(CreateTestSessionContext context) @@ -75,7 +75,7 @@ public sealed override async Task CloseTestSessionAsync( _incomingRequestCounter.Signal(); // Wait for remaining request processing - await _incomingRequestCounter.WaitAsync(context.CancellationToken); + await _incomingRequestCounter.WaitAsync(context.CancellationToken).ConfigureAwait(false); _sessionUid = null; return new CloseTestSessionResult { IsSuccess = true }; } @@ -108,7 +108,7 @@ protected virtual void Dispose(bool disposing) /// The cancellation token. protected sealed override Task DiscoverTestsAsync(VSTestDiscoverTestExecutionRequest request, IMessageBus messageBus, CancellationToken cancellationToken) - => ExecuteRequestWithRequestCountGuardAsync(async () => await SynchronizedDiscoverTestsAsync(request, messageBus, cancellationToken)); + => ExecuteRequestWithRequestCountGuardAsync(async () => await SynchronizedDiscoverTestsAsync(request, messageBus, cancellationToken).ConfigureAwait(false)); /// /// Discovers tests asynchronously with handling of concurrency. @@ -127,7 +127,7 @@ protected abstract Task SynchronizedDiscoverTestsAsync(VSTestDiscoverTestExecuti /// The cancellation token. protected sealed override Task RunTestsAsync(VSTestRunTestExecutionRequest request, IMessageBus messageBus, CancellationToken cancellationToken) - => ExecuteRequestWithRequestCountGuardAsync(async () => await SynchronizedRunTestsAsync(request, messageBus, cancellationToken)); + => ExecuteRequestWithRequestCountGuardAsync(async () => await SynchronizedRunTestsAsync(request, messageBus, cancellationToken).ConfigureAwait(false)); /// /// Runs tests asynchronously with handling of concurrency. @@ -150,20 +150,20 @@ protected sealed override Task ExecuteRequestAsync(TestExecutionRequest request, => 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(); + string[] testAssemblyPaths = [.. _getTestAssemblies().Select(x => x.Location)]; #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); + await SynchronizedDiscoverTestsAsync(vstestDiscoverRequest, messageBus, cancellationToken).ConfigureAwait(false); break; case RunTestExecutionRequest runRequest: VSTestRunTestExecutionRequest vstestRunRequest = VSTestRunTestExecutionRequestFactory.CreateRequest(runRequest, this, testAssemblyPaths, cancellationToken); - await SynchronizedRunTestsAsync(vstestRunRequest, messageBus, cancellationToken); + await SynchronizedRunTestsAsync(vstestRunRequest, messageBus, cancellationToken).ConfigureAwait(false); break; default: @@ -185,7 +185,7 @@ private async Task ExecuteRequestWithRequestCountGuardAsync(Func asyncFunc try { - await asyncFunc(); + await asyncFunc().ConfigureAwait(false); } finally { diff --git a/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/TestHostControllers/RunSettingsEnvironmentVariableProvider .cs b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/TestHostControllers/RunSettingsEnvironmentVariableProvider.cs similarity index 94% rename from src/Platform/Microsoft.Testing.Extensions.VSTestBridge/TestHostControllers/RunSettingsEnvironmentVariableProvider .cs rename to src/Platform/Microsoft.Testing.Extensions.VSTestBridge/TestHostControllers/RunSettingsEnvironmentVariableProvider.cs index b1e9e21a15..72a5fd8152 100644 --- a/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/TestHostControllers/RunSettingsEnvironmentVariableProvider .cs +++ b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/TestHostControllers/RunSettingsEnvironmentVariableProvider.cs @@ -38,17 +38,17 @@ public async Task IsEnabledAsync() return false; } - if (!_fileSystem.Exists(runsettings[0])) + if (!_fileSystem.ExistFile(runsettings[0])) { return false; } using IFileStream fileStream = _fileSystem.NewFileStream(runsettings[0], FileMode.Open, FileAccess.Read); #if NETCOREAPP - _runSettings = await XDocument.LoadAsync(fileStream.Stream, LoadOptions.None, CancellationToken.None); + _runSettings = await XDocument.LoadAsync(fileStream.Stream, LoadOptions.None, CancellationToken.None).ConfigureAwait(false); #else using StreamReader streamReader = new(fileStream.Stream); - _runSettings = XDocument.Parse(await streamReader.ReadToEndAsync()); + _runSettings = XDocument.Parse(await streamReader.ReadToEndAsync().ConfigureAwait(false)); #endif return _runSettings.Element("RunSettings")?.Element("RunConfiguration")?.Element("EnvironmentVariables") is not null; } diff --git a/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/VSTestBridgedTestFrameworkBase.cs b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/VSTestBridgedTestFrameworkBase.cs index 28481349d3..b52022ecd0 100644 --- a/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/VSTestBridgedTestFrameworkBase.cs +++ b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/VSTestBridgedTestFrameworkBase.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. -#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. - using Microsoft.Testing.Extensions.TrxReport.Abstractions; using Microsoft.Testing.Extensions.VSTestBridge.Helpers; using Microsoft.Testing.Extensions.VSTestBridge.ObjectModel; @@ -14,6 +12,7 @@ using Microsoft.Testing.Platform.Messages; using Microsoft.Testing.Platform.Requests; using Microsoft.Testing.Platform.Services; +using Microsoft.VisualStudio.TestPlatform.ObjectModel; namespace Microsoft.Testing.Extensions.VSTestBridge; @@ -58,6 +57,11 @@ protected VSTestBridgedTestFrameworkBase(IServiceProvider serviceProvider, ITest /// protected internal IServiceProvider ServiceProvider { get; } + /// + /// Gets a value indicating whether the should use instead of . + /// + protected internal virtual bool UseFullyQualifiedNameAsTestNodeUid { get; } + /// /// Gets a value indicating whether the TRX report is enabled. /// @@ -86,7 +90,7 @@ public async Task ExecuteRequestAsync(ExecuteRequestContext context) _ => Task.CompletedTask, }; - await convertedRequest; + await convertedRequest.ConfigureAwait(false); } finally { 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 index 3d9603f9a6..619611adb6 100644 --- a/src/Platform/Microsoft.Testing.Platform.MSBuild/Microsoft.Testing.Platform.MSBuild.csproj +++ b/src/Platform/Microsoft.Testing.Platform.MSBuild/Microsoft.Testing.Platform.MSBuild.csproj @@ -1,7 +1,7 @@  $(MicrosoftTestingTargetFrameworks);netstandard2.0 - PLATFORM_MSBUILD + $(DefineConstants);PLATFORM_MSBUILD $(NoWarn);NU5100 @@ -45,9 +45,9 @@ This package provides MSBuild integration of the platform, its extensions and co - - - + + + @@ -58,10 +58,6 @@ This package provides MSBuild integration of the platform, its extensions and co - - - - diff --git a/src/Platform/Microsoft.Testing.Platform.MSBuild/Tasks/DotnetMuxerLocator.cs b/src/Platform/Microsoft.Testing.Platform.MSBuild/Tasks/DotnetMuxerLocator.cs index 0b3beddffd..11f1c6910c 100644 --- a/src/Platform/Microsoft.Testing.Platform.MSBuild/Tasks/DotnetMuxerLocator.cs +++ b/src/Platform/Microsoft.Testing.Platform.MSBuild/Tasks/DotnetMuxerLocator.cs @@ -8,14 +8,22 @@ namespace Microsoft.Testing.Platform.MSBuild.Tasks; internal sealed class DotnetMuxerLocator { + // Mach-O magic numbers from https://en.wikipedia.org/wiki/Mach-O + private const uint MachOMagic32BigEndian = 0xfeedface; // 32-bit big-endian + private const uint MachOMagic64BigEndian = 0xfeedfacf; // 64-bit big-endian + private const uint MachOMagic32LittleEndian = 0xcefaedfe; // 32-bit little-endian + private const uint MachOMagic64LittleEndian = 0xcffaedfe; // 64-bit little-endian + private const uint MachOMagicFatBigEndian = 0xcafebabe; // Multi-architecture big-endian + private readonly string _muxerName; - private readonly Process _currentProcess; private readonly Action _resolutionLog; + private readonly string _currentProcessFileName; internal DotnetMuxerLocator(Action resolutionLog) { _muxerName = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? "dotnet.exe" : "dotnet"; - _currentProcess = Process.GetCurrentProcess(); + using var currentProcess = Process.GetCurrentProcess(); + _currentProcessFileName = currentProcess.MainModule!.FileName!; _resolutionLog = resolutionLog; } @@ -52,15 +60,14 @@ public bool TryGetDotnetPathByArchitecture( // If current process is the same as the target architecture we return the current process filename. if (GetCurrentProcessArchitecture() == targetArchitecture) { - string currentProcessFileName = _currentProcess.MainModule!.FileName!; - if (Path.GetFileName(currentProcessFileName) == _muxerName) + if (Path.GetFileName(_currentProcessFileName) == _muxerName) { - muxerPath = currentProcessFileName; + muxerPath = _currentProcessFileName; _resolutionLog($"DotnetHostHelper.TryGetDotnetPathByArchitecture: Target architecture is the same as the current process architecture '{targetArchitecture}', and the current process is a muxer, using that: '{muxerPath}'"); return true; } - _resolutionLog($"DotnetHostHelper.TryGetDotnetPathByArchitecture: Target architecture is the same as the current process architecture '{targetArchitecture}', but the current process is not a muxer: '{currentProcessFileName}'"); + _resolutionLog($"DotnetHostHelper.TryGetDotnetPathByArchitecture: Target architecture is the same as the current process architecture '{targetArchitecture}', but the current process is not a muxer: '{_currentProcessFileName}'"); } // We used similar approach as the runtime resolver. @@ -287,9 +294,7 @@ public bool TryGetDotnetPathByArchitecture( try { - using Stream stream = new FileStream(installLocation, FileMode.Open, FileAccess.Read); - using StreamReader streamReader = new(stream); - string content = streamReader.ReadToEnd().Trim(); + string content = File.ReadAllText(installLocation).Trim(); _resolutionLog($"DotnetHostHelper: '{installLocation}' content '{content}'"); string path = Path.Combine(content, _muxerName); _resolutionLog($"DotnetHostHelper: Muxer resolved using '{installLocation}' in '{path}'"); @@ -411,6 +416,14 @@ public bool TryGetDotnetPathByArchitecture( #pragma warning restore CA2022 // Avoid inexact read with 'Stream.Read' uint magic = BitConverter.ToUInt32(magicBytes, 0); + + // Validate magic bytes to ensure this is a valid Mach-O binary + if (magic is not (MachOMagic32BigEndian or MachOMagic64BigEndian or MachOMagic32LittleEndian or MachOMagic64LittleEndian or MachOMagicFatBigEndian)) + { + _resolutionLog($"DotnetHostHelper.GetMuxerArchitectureByMachoOnMac: Invalid Mach-O magic bytes: 0x{magic:X8}"); + return null; + } + uint cpuInfo = BitConverter.ToUInt32(cpuInfoBytes, 0); PlatformArchitecture? architecture = (MacOsCpuType)cpuInfo switch { diff --git a/src/Platform/Microsoft.Testing.Platform.MSBuild/Tasks/FailedTestHelper.cs b/src/Platform/Microsoft.Testing.Platform.MSBuild/Tasks/FailedTestHelper.cs index 0c6e51a65f..138f407108 100644 --- a/src/Platform/Microsoft.Testing.Platform.MSBuild/Tasks/FailedTestHelper.cs +++ b/src/Platform/Microsoft.Testing.Platform.MSBuild/Tasks/FailedTestHelper.cs @@ -71,17 +71,16 @@ internal static void FromFailedTest(this FailedTestInfoRequest failedTestInfoReq string nameAndPlace = place == null ? $"{failedTestInfoRequest.DisplayName} ({failedTestInfoRequest.Duration})" : $"{failedTestInfoRequest.DisplayName} ({failedTestInfoRequest.Duration}): {place}"; - string? singleLineError = JoinSingleLineAndShorten(nameAndPlace, failedTestInfoRequest.ErrorMessage); - message = singleLineError!; + message = JoinSingleLineAndShorten(nameAndPlace, failedTestInfoRequest.ErrorMessage); } } - private static string? JoinSingleLineAndShorten(string first, string? second) + private static string JoinSingleLineAndShorten(string first, string? second) => second == null ? SingleLineAndShorten(first) : SingleLineAndShorten(first) + " " + SingleLineAndShorten(second); - private static string? SingleLineAndShorten(string? text) - => text == null ? null : (text.Length <= 1000 ? text : text[..1000]).Replace('\r', ' ').Replace('\n', ' '); + private static string SingleLineAndShorten(string text) => + (text.Length <= 1000 ? text : text[..1000]).Replace('\r', ' ').Replace('\n', ' '); } diff --git a/src/Platform/Microsoft.Testing.Platform.MSBuild/Tasks/InvokeTestingPlatformTask.cs b/src/Platform/Microsoft.Testing.Platform.MSBuild/Tasks/InvokeTestingPlatformTask.cs index ed0cbe17ec..7f75710fe4 100644 --- a/src/Platform/Microsoft.Testing.Platform.MSBuild/Tasks/InvokeTestingPlatformTask.cs +++ b/src/Platform/Microsoft.Testing.Platform.MSBuild/Tasks/InvokeTestingPlatformTask.cs @@ -32,7 +32,7 @@ public class InvokeTestingPlatformTask : Build.Utilities.ToolTask, IDisposable private readonly IFileSystem _fileSystem; private readonly PipeNameDescription _pipeNameDescription; private readonly CancellationTokenSource _waitForConnections = new(); - private readonly List _connections = new(); + private readonly List _connections = []; private readonly StringBuilder _output = new(); private readonly Lock _initLock = new(); private readonly Architecture _currentProcessArchitecture = RuntimeInformation.ProcessArchitecture; @@ -54,7 +54,7 @@ public InvokeTestingPlatformTask() Debugger.Launch(); } - _pipeNameDescription = NamedPipeServer.GetPipeName(Guid.NewGuid().ToString("N")); + _pipeNameDescription = NamedPipeServer.GetPipeName(Guid.NewGuid().ToString("N"), new SystemEnvironment()); } internal InvokeTestingPlatformTask(IFileSystem fileSystem) => _fileSystem = fileSystem; @@ -207,7 +207,7 @@ protected override string ToolName } Log.LogMessage(MessageImportance.Low, $"Current process architecture '{_currentProcessArchitecture}'. Requested test architecture '{TestArchitecture.ItemSpec}'"); - PlatformArchitecture targetArchitecture = EnumPolyfill.Parse(TestArchitecture.ItemSpec, ignoreCase: true); + PlatformArchitecture targetArchitecture = Enum.Parse(TestArchitecture.ItemSpec, ignoreCase: true); StringBuilder resolutionLog = new(); DotnetMuxerLocator dotnetMuxerLocator = new(log => resolutionLog.AppendLine(log)); if (dotnetMuxerLocator.TryGetDotnetPathByArchitecture(targetArchitecture, out string? dotnetPath)) @@ -241,7 +241,7 @@ protected override string ToolName } private bool IsCurrentProcessArchitectureCompatible() => - _currentProcessArchitecture == EnumPolyfill.Parse(TestArchitecture.ItemSpec, ignoreCase: true); + _currentProcessArchitecture == Enum.Parse(TestArchitecture.ItemSpec, ignoreCase: true); private string? TryGetRunCommand() { @@ -352,7 +352,7 @@ protected override void ProcessStarted() pipeServer.RegisterSerializer(new VoidResponseSerializer(), typeof(VoidResponse)); pipeServer.RegisterSerializer(new FailedTestInfoRequestSerializer(), typeof(FailedTestInfoRequest)); pipeServer.RegisterSerializer(new RunSummaryInfoRequestSerializer(), typeof(RunSummaryInfoRequest)); - await pipeServer.WaitConnectionAsync(_waitForConnections.Token); + await pipeServer.WaitConnectionAsync(_waitForConnections.Token).ConfigureAwait(false); _connections.Add(pipeServer); Log.LogMessage(MessageImportance.Low, $"Client connected to '{_pipeNameDescription.Name}'"); } @@ -433,6 +433,10 @@ protected override bool HandleTaskExecutionErrors() private Task HandleRequestAsync(IRequest request) { + // For the case, of orchestrator (e.g, Retry), we can get ModuleInfoRequest from the orchestrator itself. + // If there is no orchestrator or the orchestrator didn't send ModuleInfoRequest, we will get it from the first test host. + // For the case of retry, the request is different between the orchestrator and the test host. + // More specifically, the results directory is different (orchestrator points to original, while test host points to the specific retry results directory). if (request is ModuleInfoRequest moduleInfo) { if (_moduleInfo is null) diff --git a/src/Platform/Microsoft.Testing.Platform.MSBuild/Tasks/TestingPlatformAutoRegisteredExtensions.cs b/src/Platform/Microsoft.Testing.Platform.MSBuild/Tasks/TestingPlatformAutoRegisteredExtensions.cs index 818749ffad..f1d1018d35 100644 --- a/src/Platform/Microsoft.Testing.Platform.MSBuild/Tasks/TestingPlatformAutoRegisteredExtensions.cs +++ b/src/Platform/Microsoft.Testing.Platform.MSBuild/Tasks/TestingPlatformAutoRegisteredExtensions.cs @@ -156,7 +156,7 @@ private static ITaskItem[] Reorder(ITaskItem[] items) result.Add(items[wellKnownBuilderHook_MicrosoftTestingPlatformExtensions_index]); } - return result.ToArray(); + return [.. result]; } private static void GenerateCode(string language, string? rootNamespace, ITaskItem[] taskItems, ITaskItem testingPlatformEntryPointSourcePath, IFileSystem fileSystem, TaskLoggingHelper taskLoggingHelper) 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 index 882b0396c6..8587250576 100644 --- 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 @@ -4,7 +4,7 @@ - $(MSBuildThisFileDirectory)..\_MSBuildTasks\netstandard2.0\ + $(MSBuildThisFileDirectory)..\_MSBuildTasks\netstandard2.0\ @@ -331,7 +331,7 @@ False true - + @@ -88,26 +85,9 @@ This package provides the core platform and the .NET implementation of the proto - - - - - <_TemplateProperties>Version=$(Version) - - - <_TemplateCsproj Include="$(MSBuildProjectDirectory)/PlatformVersion.cs.template" Destination="$(IntermediateOutputPath)/PlatformVersion.cs" /> - - - - - - - - - diff --git a/src/Platform/Microsoft.Testing.Platform/OutputDevice/BrowserOutputDevice.cs b/src/Platform/Microsoft.Testing.Platform/OutputDevice/BrowserOutputDevice.cs index e215879c04..87a62f15d4 100644 --- a/src/Platform/Microsoft.Testing.Platform/OutputDevice/BrowserOutputDevice.cs +++ b/src/Platform/Microsoft.Testing.Platform/OutputDevice/BrowserOutputDevice.cs @@ -93,7 +93,7 @@ public async Task InitializeAsync() { ConsoleLog(PlatformResources.CancellingTestSession); return Task.CompletedTask; - }); + }).ConfigureAwait(false); public Type[] DataTypesConsumed { get; } = [ @@ -103,23 +103,23 @@ public async Task InitializeAsync() ]; /// - public string Uid { get; } = nameof(BrowserOutputDevice); + public string Uid => nameof(BrowserOutputDevice); /// - public string Version { get; } = AppVersion.DefaultSemVer; + public string Version => AppVersion.DefaultSemVer; /// - public string DisplayName { get; } = "Test Platform Browser Console Service"; + public string DisplayName => "Test Platform Browser Console Service"; /// - public string Description { get; } = "Test Platform default browser console service"; + public string Description => "Test Platform default browser console service"; /// public Task IsEnabledAsync() => Task.FromResult(true); public async Task DisplayBannerAsync(string? bannerMessage) { - using (await _asyncMonitor.LockAsync(TimeoutHelper.DefaultHangTimeSpanTimeout)) + using (await _asyncMonitor.LockAsync(TimeoutHelper.DefaultHangTimeSpanTimeout).ConfigureAwait(false)) { if (_bannerDisplayed) { @@ -151,7 +151,7 @@ public async Task DisplayBannerAsync(string? bannerMessage) if (_platformInformation.BuildDate is { } buildDate) { - stringBuilder.Append(CultureInfo.InvariantCulture, $" (UTC {buildDate.UtcDateTime.ToShortDateString()})"); + stringBuilder.Append(CultureInfo.InvariantCulture, $" (UTC {buildDate.UtcDateTime:d})"); } if (_runtimeFeature.IsDynamicCodeSupported) @@ -202,7 +202,7 @@ private static void AppendAssemblyLinkTargetFrameworkAndArchitecture(IConsole co public async Task DisplayAfterSessionEndRunAsync() { - using (await _asyncMonitor.LockAsync(TimeoutHelper.DefaultHangTimeSpanTimeout)) + using (await _asyncMonitor.LockAsync(TimeoutHelper.DefaultHangTimeSpanTimeout).ConfigureAwait(false)) { if (_firstCallTo_OnSessionStartingAsync) { @@ -257,7 +257,7 @@ public Task OnTestSessionStartingAsync(SessionUid sessionUid, CancellationToken /// The data to be displayed. public async Task DisplayAsync(IOutputDeviceDataProducer producer, IOutputDeviceData data) { - using (await _asyncMonitor.LockAsync(TimeoutHelper.DefaultHangTimeSpanTimeout)) + using (await _asyncMonitor.LockAsync(TimeoutHelper.DefaultHangTimeSpanTimeout).ConfigureAwait(false)) { switch (data) { @@ -398,7 +398,7 @@ public async Task HandleProcessRoleAsync(TestProcessRole processRole) { await _policiesService.RegisterOnMaxFailedTestsCallbackAsync( async (maxFailedTests, _) => await DisplayAsync( - this, new TextOutputDeviceData(string.Format(CultureInfo.InvariantCulture, PlatformResources.ReachedMaxFailedTestsMessage, maxFailedTests)))); + this, new TextOutputDeviceData(string.Format(CultureInfo.InvariantCulture, PlatformResources.ReachedMaxFailedTestsMessage, maxFailedTests))).ConfigureAwait(false)).ConfigureAwait(false); } } } diff --git a/src/Platform/Microsoft.Testing.Platform/OutputDevice/OutputDeviceManager.cs b/src/Platform/Microsoft.Testing.Platform/OutputDevice/OutputDeviceManager.cs index 87ae5097ea..bd791add93 100644 --- a/src/Platform/Microsoft.Testing.Platform/OutputDevice/OutputDeviceManager.cs +++ b/src/Platform/Microsoft.Testing.Platform/OutputDevice/OutputDeviceManager.cs @@ -26,7 +26,7 @@ internal async Task BuildAsync(ServiceProvider serviceProvide : _platformOutputDeviceFactory(serviceProvider); // If the externally provided output device is not enabled, we opt-in the default terminal output device. - if (_platformOutputDeviceFactory is not null && !await nonServerOutputDevice.IsEnabledAsync()) + if (_platformOutputDeviceFactory is not null && !await nonServerOutputDevice.IsEnabledAsync().ConfigureAwait(false)) { nonServerOutputDevice = GetDefaultTerminalOutputDevice(serviceProvider); } @@ -62,5 +62,6 @@ public static IPlatformOutputDevice GetDefaultTerminalOutputDevice(ServiceProvid serviceProvider.GetFileLoggerInformation(), serviceProvider.GetLoggerFactory(), serviceProvider.GetClock(), - serviceProvider.GetRequiredService()); + serviceProvider.GetRequiredService(), + serviceProvider.GetTestApplicationCancellationTokenSource()); } diff --git a/src/Platform/Microsoft.Testing.Platform/OutputDevice/ProxyOutputDevice.cs b/src/Platform/Microsoft.Testing.Platform/OutputDevice/ProxyOutputDevice.cs index b2d0b2ed4c..4987ac93a8 100644 --- a/src/Platform/Microsoft.Testing.Platform/OutputDevice/ProxyOutputDevice.cs +++ b/src/Platform/Microsoft.Testing.Platform/OutputDevice/ProxyOutputDevice.cs @@ -21,37 +21,37 @@ public ProxyOutputDevice(IPlatformOutputDevice originalOutputDevice, ServerModeP public async Task DisplayAsync(IOutputDeviceDataProducer producer, IOutputDeviceData data) { - await OriginalOutputDevice.DisplayAsync(producer, data); + await OriginalOutputDevice.DisplayAsync(producer, data).ConfigureAwait(false); if (_serverModeOutputDevice is not null) { - await _serverModeOutputDevice.DisplayAsync(producer, data); + await _serverModeOutputDevice.DisplayAsync(producer, data).ConfigureAwait(false); } } internal async Task DisplayBannerAsync(string? bannerMessage) { - await OriginalOutputDevice.DisplayBannerAsync(bannerMessage); + await OriginalOutputDevice.DisplayBannerAsync(bannerMessage).ConfigureAwait(false); if (_serverModeOutputDevice is not null) { - await _serverModeOutputDevice.DisplayBannerAsync(bannerMessage); + await _serverModeOutputDevice.DisplayBannerAsync(bannerMessage).ConfigureAwait(false); } } internal async Task DisplayBeforeSessionStartAsync() { - await OriginalOutputDevice.DisplayBeforeSessionStartAsync(); + await OriginalOutputDevice.DisplayBeforeSessionStartAsync().ConfigureAwait(false); if (_serverModeOutputDevice is not null) { - await _serverModeOutputDevice.DisplayBeforeSessionStartAsync(); + await _serverModeOutputDevice.DisplayBeforeSessionStartAsync().ConfigureAwait(false); } } internal async Task DisplayAfterSessionEndRunAsync() { - await OriginalOutputDevice.DisplayAfterSessionEndRunAsync(); + await OriginalOutputDevice.DisplayAfterSessionEndRunAsync().ConfigureAwait(false); if (_serverModeOutputDevice is not null) { - await _serverModeOutputDevice.DisplayAfterSessionEndRunAsync(); + await _serverModeOutputDevice.DisplayAfterSessionEndRunAsync().ConfigureAwait(false); } } @@ -59,16 +59,16 @@ internal async Task InitializeAsync(ServerTestHost serverTestHost) { if (_serverModeOutputDevice is not null) { - await _serverModeOutputDevice.InitializeAsync(serverTestHost); + await _serverModeOutputDevice.InitializeAsync(serverTestHost).ConfigureAwait(false); } } internal async Task HandleProcessRoleAsync(TestProcessRole processRole) { - await OriginalOutputDevice.HandleProcessRoleAsync(processRole); + await OriginalOutputDevice.HandleProcessRoleAsync(processRole).ConfigureAwait(false); if (_serverModeOutputDevice is not null) { - await _serverModeOutputDevice.HandleProcessRoleAsync(processRole); + await _serverModeOutputDevice.HandleProcessRoleAsync(processRole).ConfigureAwait(false); } } } diff --git a/src/Platform/Microsoft.Testing.Platform/OutputDevice/Terminal/AnsiDetector.cs b/src/Platform/Microsoft.Testing.Platform/OutputDevice/Terminal/AnsiDetector.cs index 5dd4b3e884..9c1b06e283 100644 --- a/src/Platform/Microsoft.Testing.Platform/OutputDevice/Terminal/AnsiDetector.cs +++ b/src/Platform/Microsoft.Testing.Platform/OutputDevice/Terminal/AnsiDetector.cs @@ -13,7 +13,7 @@ namespace Microsoft.Testing.Platform.OutputDevice.Terminal; internal static class AnsiDetector { private static readonly Regex[] TerminalsRegexes = - { + [ new("^xterm"), // xterm, PuTTY, Mintty new("^rxvt"), // RXVT new("^(?!eterm-color).*eterm.*"), // Accepts eterm, but not eterm-color, which does not support moving the cursor, see #9950. @@ -30,8 +30,8 @@ internal static class AnsiDetector new("konsole"), // Konsole new("bvterm"), // Bitvise SSH Client new("^st-256color"), // Suckless Simple Terminal, st - new("alacritty"), // Alacritty - }; + new("alacritty") // Alacritty + ]; public static bool IsAnsiSupported(string? termType) => !RoslynString.IsNullOrEmpty(termType) && TerminalsRegexes.Any(regex => regex.IsMatch(termType)); diff --git a/src/Platform/Microsoft.Testing.Platform/OutputDevice/Terminal/AnsiTerminal.cs b/src/Platform/Microsoft.Testing.Platform/OutputDevice/Terminal/AnsiTerminal.cs index 4e8e511e22..20ffd4af20 100644 --- a/src/Platform/Microsoft.Testing.Platform/OutputDevice/Terminal/AnsiTerminal.cs +++ b/src/Platform/Microsoft.Testing.Platform/OutputDevice/Terminal/AnsiTerminal.cs @@ -16,8 +16,8 @@ internal sealed class AnsiTerminal : ITerminal /// File extensions that we will link to directly, all other files /// are linked to their directory, to avoid opening dlls, or executables. /// - private static readonly string[] KnownFileExtensions = new string[] - { + private static readonly string[] KnownFileExtensions = + [ // code files ".cs", ".vb", @@ -33,8 +33,8 @@ internal sealed class AnsiTerminal : ITerminal ".nunit", ".trx", ".xml", - ".xunit", - }; + ".xunit" + ]; private readonly IConsole _console; private readonly string? _baseDirectory; @@ -43,10 +43,10 @@ internal sealed class AnsiTerminal : ITerminal private bool _isBatching; private AnsiTerminalTestProgressFrame _currentFrame = new(0, 0); - public AnsiTerminal(IConsole console, string? baseDirectory) + public AnsiTerminal(IConsole console) { _console = console; - _baseDirectory = baseDirectory ?? Directory.GetCurrentDirectory(); + _baseDirectory = Directory.GetCurrentDirectory(); // Output ansi code to get spinner on top of a terminal, to indicate in-progress task. // https://github.com/dotnet/msbuild/issues/8958: iTerm2 treats ;9 code to post a notification instead, so disable progress reporting on Mac. diff --git a/src/Platform/Microsoft.Testing.Platform/OutputDevice/Terminal/AnsiTerminalTestProgressFrame.cs b/src/Platform/Microsoft.Testing.Platform/OutputDevice/Terminal/AnsiTerminalTestProgressFrame.cs index 6489512a6b..85606f5b0c 100644 --- a/src/Platform/Microsoft.Testing.Platform/OutputDevice/Terminal/AnsiTerminalTestProgressFrame.cs +++ b/src/Platform/Microsoft.Testing.Platform/OutputDevice/Terminal/AnsiTerminalTestProgressFrame.cs @@ -30,44 +30,62 @@ public void AppendTestWorkerProgress(TestProgressState progress, RenderedProgres int nonReservedWidth = Width - (durationString.Length + 2); + int discovered = progress.DiscoveredTests; int passed = progress.PassedTests; int failed = progress.FailedTests; int skipped = progress.SkippedTests; int charsTaken = 0; - terminal.Append('['); - charsTaken++; - terminal.SetColor(TerminalColor.Green); - terminal.Append('✓'); - charsTaken++; - string passedText = passed.ToString(CultureInfo.CurrentCulture); - terminal.Append(passedText); - charsTaken += passedText.Length; - terminal.ResetColor(); - - terminal.Append('/'); - charsTaken++; - - terminal.SetColor(TerminalColor.Red); - terminal.Append('x'); - charsTaken++; - string failedText = failed.ToString(CultureInfo.CurrentCulture); - terminal.Append(failedText); - charsTaken += failedText.Length; - terminal.ResetColor(); - - terminal.Append('/'); - charsTaken++; - - terminal.SetColor(TerminalColor.Yellow); - terminal.Append('↓'); - charsTaken++; - string skippedText = skipped.ToString(CultureInfo.CurrentCulture); - terminal.Append(skippedText); - charsTaken += skippedText.Length; - terminal.ResetColor(); - terminal.Append(']'); - charsTaken++; + if (!progress.IsDiscovery) + { + terminal.Append('['); + charsTaken++; + terminal.SetColor(TerminalColor.DarkGreen); + terminal.Append('✓'); + charsTaken++; + string passedText = passed.ToString(CultureInfo.CurrentCulture); + terminal.Append(passedText); + charsTaken += passedText.Length; + terminal.ResetColor(); + + terminal.Append('/'); + charsTaken++; + + terminal.SetColor(TerminalColor.DarkRed); + terminal.Append('x'); + charsTaken++; + string failedText = failed.ToString(CultureInfo.CurrentCulture); + terminal.Append(failedText); + charsTaken += failedText.Length; + terminal.ResetColor(); + + terminal.Append('/'); + charsTaken++; + + terminal.SetColor(TerminalColor.DarkYellow); + terminal.Append('↓'); + charsTaken++; + string skippedText = skipped.ToString(CultureInfo.CurrentCulture); + terminal.Append(skippedText); + charsTaken += skippedText.Length; + terminal.ResetColor(); + terminal.Append(']'); + charsTaken++; + } + else + { + string discoveredText = discovered.ToString(CultureInfo.CurrentCulture); + terminal.Append('['); + charsTaken++; + terminal.SetColor(TerminalColor.DarkMagenta); + terminal.Append('+'); + charsTaken++; + terminal.Append(discoveredText); + charsTaken += discoveredText.Length; + terminal.ResetColor(); + terminal.Append(']'); + charsTaken++; + } terminal.Append(' '); charsTaken++; @@ -308,7 +326,7 @@ private List GenerateLinesToRender(TestProgressState?[] progress) // Note: We want to render the list of active tests, but this can easily fill up the full screen. // As such, we should balance the number of active tests shown per project. // We do this by distributing the remaining lines for each projects. - TestProgressState[] progressItems = progress.OfType().ToArray(); + TestProgressState[] progressItems = [.. progress.OfType()]; int linesToDistribute = (int)(Height * 0.7) - 1 - progressItems.Length; var detailItems = new IEnumerable[progressItems.Length]; IEnumerable sortedItemsIndices = Enumerable.Range(0, progressItems.Length).OrderBy(i => progressItems[i].TestNodeResultsState?.Count ?? 0); @@ -317,7 +335,7 @@ private List GenerateLinesToRender(TestProgressState?[] progress) { detailItems[sortedItemIndex] = progressItems[sortedItemIndex].TestNodeResultsState?.GetRunningTasks( linesToDistribute / progressItems.Length) - ?? Array.Empty(); + ?? []; } for (int progressI = 0; progressI < progressItems.Length; progressI++) diff --git a/src/Platform/Microsoft.Testing.Platform/OutputDevice/Terminal/ExceptionFlattener.cs b/src/Platform/Microsoft.Testing.Platform/OutputDevice/Terminal/ExceptionFlattener.cs index 5e0a406ab8..929a2d6ed4 100644 --- a/src/Platform/Microsoft.Testing.Platform/OutputDevice/Terminal/ExceptionFlattener.cs +++ b/src/Platform/Microsoft.Testing.Platform/OutputDevice/Terminal/ExceptionFlattener.cs @@ -9,7 +9,7 @@ internal static FlatException[] Flatten(string? errorMessage, Exception? excepti { if (errorMessage is null && exception is null) { - return Array.Empty(); + return []; } string? message = !RoslynString.IsNullOrWhiteSpace(errorMessage) ? errorMessage : exception?.Message; @@ -17,10 +17,7 @@ internal static FlatException[] Flatten(string? errorMessage, Exception? excepti string? stackTrace = exception?.StackTrace; var flatException = new FlatException(message, type, stackTrace); - List flatExceptions = new() - { - flatException, - }; + List flatExceptions = [flatException]; // Add all inner exceptions. This will flatten top level AggregateExceptions, // and all AggregateExceptions that are directly in AggregateExceptions, but won't expand @@ -45,7 +42,7 @@ internal static FlatException[] Flatten(string? errorMessage, Exception? excepti } } - return flatExceptions.ToArray(); + return [.. flatExceptions]; } } diff --git a/src/Platform/Microsoft.Testing.Platform/OutputDevice/Terminal/NonAnsiTerminal.cs b/src/Platform/Microsoft.Testing.Platform/OutputDevice/Terminal/NonAnsiTerminal.cs index 11af53dc1e..78b26ac13d 100644 --- a/src/Platform/Microsoft.Testing.Platform/OutputDevice/Terminal/NonAnsiTerminal.cs +++ b/src/Platform/Microsoft.Testing.Platform/OutputDevice/Terminal/NonAnsiTerminal.cs @@ -2,7 +2,6 @@ // 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.Resources; namespace Microsoft.Testing.Platform.OutputDevice.Terminal; @@ -10,143 +9,48 @@ namespace Microsoft.Testing.Platform.OutputDevice.Terminal; /// Non-ANSI terminal that writes text using the standard Console.Foreground color capabilities to stay compatible with /// standard Windows command line, and other command lines that are not capable of ANSI, or when output is redirected. /// -internal sealed class NonAnsiTerminal : ITerminal +internal sealed class NonAnsiTerminal : SimpleTerminal { - private readonly IConsole _console; private readonly ConsoleColor _defaultForegroundColor; - private bool _isBatching; - private object? _batchingLock; + private bool? _colorNotSupported; public NonAnsiTerminal(IConsole console) - { - _console = console; - _defaultForegroundColor = IsForegroundColorNotSupported() ? ConsoleColor.Black : _console.GetForegroundColor(); - } - -#pragma warning disable CA1416 // Validate platform compatibility - public int Width => _console.IsOutputRedirected ? int.MaxValue : _console.BufferWidth; - - public int Height => _console.IsOutputRedirected ? int.MaxValue : _console.BufferHeight; -#pragma warning restore CA1416 // Validate platform compatibility - - public void Append(char value) - => _console.Write(value); - - public void Append(string value) - => _console.Write(value); - - public void AppendLine() - => _console.WriteLine(); - - public void AppendLine(string value) - => _console.WriteLine(value); - - public void AppendLink(string path, int? lineNumber) - { - Append(path); - if (lineNumber.HasValue) - { - Append($":{lineNumber}"); - } - } + : base(console) + => _defaultForegroundColor = IsForegroundColorNotSupported() ? ConsoleColor.Black : console.GetForegroundColor(); - public void SetColor(TerminalColor color) + public override void SetColor(TerminalColor color) { if (IsForegroundColorNotSupported()) { return; } - _console.SetForegroundColor(ToConsoleColor(color)); + Console.SetForegroundColor(ToConsoleColor(color)); } - public void ResetColor() + public override void ResetColor() { if (IsForegroundColorNotSupported()) { return; } - _console.SetForegroundColor(_defaultForegroundColor); - } - - public void ShowCursor() - { - // nop + Console.SetForegroundColor(_defaultForegroundColor); } - public void HideCursor() - { - // nop - } - - // TODO: Refactor NonAnsiTerminal and AnsiTerminal such that we don't need StartUpdate/StopUpdate. - // It's much better if we use lock C# keyword instead of manually calling Monitor.Enter/Exit - // Using lock also ensures we don't accidentally have `await`s in between that could cause Exit to be on a different thread. - public void StartUpdate() + [SupportedOSPlatformGuard("android")] + [SupportedOSPlatformGuard("ios")] + [SupportedOSPlatformGuard("tvos")] + [SupportedOSPlatformGuard("browser")] + private bool IsForegroundColorNotSupported() { - if (_isBatching) - { - throw new InvalidOperationException(PlatformResources.ConsoleIsAlreadyInBatchingMode); - } - - bool lockTaken = false; - - // We store Console.Out in a field to make sure we will be doing - // the Monitor.Exit call on the same instance. - _batchingLock = Console.Out; - - // Note that we need to lock on System.Out for batching to work correctly. - // Consider the following scenario: - // 1. We call StartUpdate - // 2. We call a Write("A") - // 3. User calls Console.Write("B") from another thread. - // 4. We call a Write("C"). - // 5. We call StopUpdate. - // The expectation is that we see either ACB, or BAC, but not ABC. - // Basically, when doing batching, we want to ensure that everything we write is - // written continuously, without anything in-between. - // One option (and we used to do it), is that we append to a StringBuilder while batching - // Then at StopUpdate, we write the whole string at once. - // This works to some extent, but we cannot get it to work when SetColor kicks in. - // Console methods will internally lock on Console.Out, so we are locking on the same thing. - // This locking is the easiest way to get coloring to work correctly while preventing - // interleaving with user's calls to Console.Write methods. - // One extra note: - // It's very important to lock on Console.Out (the current Console.Out). - // Consider the following scenario: - // 1. SystemConsole captures the original Console.Out set by runtime. - // 2. Framework author sets his own Console.Out which wraps the original Console.Out. - // 3. Two threads are writing concurrently: - // - One thread is writing using Console.Write* APIs, which will use the Console.Out set by framework author. - // - The other thread is writing using NonAnsiTerminal. - // 4. **If** we lock the original Console.Out. The following may happen (subject to race) [NOT THE CURRENT CASE - imaginary situation if we lock on the original Console.Out]: - // - First thread enters the Console.Write, which will acquire the lock for the current Console.Out (set by framework author). - // - Second thread executes StartUpdate, and acquires the lock for the original Console.Out. - // - First thread continues in the Write implementation of the framework author, which tries to run Console.Write on the original Console.Out. - // - First thread can't make any progress, because the second thread is holding the lock already. - // - Second thread continues execution, and reaches into runtime code (ConsolePal.WriteFromConsoleStream - on Unix) which tries to acquire the lock for the current Console.Out (set by framework author). - // - (see https://github.com/dotnet/runtime/blob/8a9d492444f06df20fcc5dfdcf7a6395af18361f/src/libraries/System.Console/src/System/ConsolePal.Unix.cs#L963) - // - No thread can progress. - // - Basically, what happened is that the first thread acquires the lock for current Console.Out, then for the original Console.Out. - // - while the second thread acquires the lock for the original Console.Out, then for the current Console.Out. - // - That's a typical deadlock where two threads are acquiring two locks in reverse order. - // 5. By locking the *current* Console.Out, we avoid the situation described in 4. - Monitor.Enter(_batchingLock, ref lockTaken); - if (!lockTaken) - { - // Can this happen? :/ - throw new InvalidOperationException(); - } - - _isBatching = true; - } + _colorNotSupported ??= RuntimeInformation.IsOSPlatform(OSPlatform.Create("ANDROID")) || + RuntimeInformation.IsOSPlatform(OSPlatform.Create("IOS")) || + RuntimeInformation.IsOSPlatform(OSPlatform.Create("TVOS")) || + RuntimeInformation.IsOSPlatform(OSPlatform.Create("WASI")) || + RuntimeInformation.IsOSPlatform(OSPlatform.Create("BROWSER")); - public void StopUpdate() - { - Monitor.Exit(_batchingLock!); - _batchingLock = null; - _isBatching = false; + return _colorNotSupported.Value; } private ConsoleColor ToConsoleColor(TerminalColor color) => color switch @@ -170,111 +74,4 @@ public void StopUpdate() TerminalColor.White => ConsoleColor.White, _ => _defaultForegroundColor, }; - - public void EraseProgress() - { - // nop - } - - public void RenderProgress(TestProgressState?[] progress) - { - int count = 0; - foreach (TestProgressState? p in progress) - { - if (p == null) - { - continue; - } - - count++; - - string durationString = HumanReadableDurationFormatter.Render(p.Stopwatch.Elapsed); - - int passed = p.PassedTests; - int failed = p.FailedTests; - int skipped = p.SkippedTests; - - // Use just ascii here, so we don't put too many restrictions on fonts needing to - // properly show unicode, or logs being saved in particular encoding. - Append('['); - SetColor(TerminalColor.DarkGreen); - Append('+'); - Append(passed.ToString(CultureInfo.CurrentCulture)); - ResetColor(); - - Append('/'); - - SetColor(TerminalColor.DarkRed); - Append('x'); - Append(failed.ToString(CultureInfo.CurrentCulture)); - ResetColor(); - - Append('/'); - - SetColor(TerminalColor.DarkYellow); - Append('?'); - Append(skipped.ToString(CultureInfo.CurrentCulture)); - ResetColor(); - Append(']'); - - Append(' '); - Append(p.AssemblyName); - - if (p.TargetFramework != null || p.Architecture != null) - { - Append(" ("); - if (p.TargetFramework != null) - { - Append(p.TargetFramework); - Append('|'); - } - - if (p.Architecture != null) - { - Append(p.Architecture); - } - - Append(')'); - } - - TestDetailState? activeTest = p.TestNodeResultsState?.GetRunningTasks(1).FirstOrDefault(); - if (!RoslynString.IsNullOrWhiteSpace(activeTest?.Text)) - { - Append(" - "); - Append(activeTest.Text); - Append(' '); - } - - Append(durationString); - - AppendLine(); - } - - // Do not render empty lines when there is nothing to show. - if (count > 0) - { - AppendLine(); - } - } - - public void StartBusyIndicator() - { - // nop - } - - public void StopBusyIndicator() - { - // nop - } - - [SupportedOSPlatformGuard("android")] - [SupportedOSPlatformGuard("ios")] - [SupportedOSPlatformGuard("tvos")] - [SupportedOSPlatformGuard("browser")] - private static bool IsForegroundColorNotSupported() - => RuntimeInformation.IsOSPlatform(OSPlatform.Create("ANDROID")) || - RuntimeInformation.IsOSPlatform(OSPlatform.Create("IOS")) || - RuntimeInformation.IsOSPlatform(OSPlatform.Create("TVOS")) || - RuntimeInformation.IsOSPlatform(OSPlatform.Create("WASI")) || - RuntimeInformation.IsOSPlatform(OSPlatform.Create("BROWSER")); } diff --git a/src/Platform/Microsoft.Testing.Platform/OutputDevice/Terminal/SimpleAnsiTerminal.cs b/src/Platform/Microsoft.Testing.Platform/OutputDevice/Terminal/SimpleAnsiTerminal.cs new file mode 100644 index 0000000000..12b4f4eee0 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Platform/OutputDevice/Terminal/SimpleAnsiTerminal.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 Microsoft.Testing.Platform.Helpers; + +namespace Microsoft.Testing.Platform.OutputDevice.Terminal; + +/// +/// Simple terminal that uses 4-bit ANSI for colors but does not move cursor and does not do other fancy stuff to stay compatible with CI systems like AzDO. +/// The colors are set on start of every line to properly color multiline strings in AzDO output. +/// +internal sealed class SimpleAnsiTerminal : SimpleTerminal +{ + private string? _foregroundColor; + private bool _prependColor; + + public SimpleAnsiTerminal(IConsole console) + : base(console) + { + } + + public override void Append(string value) + { + // Previous write appended line, so we need to prepend color. + if (_prependColor) + { + Console.Write(_foregroundColor); + // This line is not adding new line at the end, so we don't need to prepend color on next line. + _prependColor = false; + } + + Console.Write(SetColorPerLine(value)); + } + + public override void AppendLine(string value) + { + // Previous write appended line, so we need to prepend color. + if (_prependColor) + { + Console.Write(_foregroundColor); + } + + Console.WriteLine(SetColorPerLine(value)); + // This call appended new line so the next write to console needs to prepend color. + _prependColor = true; + } + + public override void SetColor(TerminalColor color) + { + string setColor = $"{AnsiCodes.CSI}{(int)color}{AnsiCodes.SetColor}"; + _foregroundColor = setColor; + Console.Write(setColor); + // This call set the color for current line, no need to prepend on next write. + _prependColor = false; + } + + public override void ResetColor() + { + _foregroundColor = null; + _prependColor = false; + Console.Write(AnsiCodes.SetDefaultColor); + } + + private string? SetColorPerLine(string value) + => _foregroundColor == null ? value : value.Replace("\n", $"\n{_foregroundColor}"); +} diff --git a/src/Platform/Microsoft.Testing.Platform/OutputDevice/Terminal/SimpleTerminalBase.cs b/src/Platform/Microsoft.Testing.Platform/OutputDevice/Terminal/SimpleTerminalBase.cs new file mode 100644 index 0000000000..a4620f0df4 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Platform/OutputDevice/Terminal/SimpleTerminalBase.cs @@ -0,0 +1,239 @@ +// 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.Resources; + +namespace Microsoft.Testing.Platform.OutputDevice.Terminal; + +internal abstract class SimpleTerminal : ITerminal +{ + private object? _batchingLock; + private bool _isBatching; + + public SimpleTerminal(IConsole console) + => Console = console; + +#pragma warning disable CA1416 // Validate platform compatibility + public int Width => Console.IsOutputRedirected ? int.MaxValue : Console.BufferWidth; + + public int Height => Console.IsOutputRedirected ? int.MaxValue : Console.BufferHeight; + + protected IConsole Console { get; } + + public void Append(char value) + => Console.Write(value); + + public virtual void Append(string value) + => Console.Write(value); + + public void AppendLine() + => Console.WriteLine(); + + public virtual void AppendLine(string value) + => Console.WriteLine(value); + + public void AppendLink(string path, int? lineNumber) + { + Append(path); + if (lineNumber.HasValue) + { + Append($":{lineNumber}"); + } + } + + public void EraseProgress() + { + // nop + } + + public void HideCursor() + { + // nop + } + + public void RenderProgress(TestProgressState?[] progress) + { + int count = 0; + foreach (TestProgressState? p in progress) + { + if (p == null) + { + continue; + } + + count++; + + string durationString = HumanReadableDurationFormatter.Render(p.Stopwatch.Elapsed); + + int discovered = p.DiscoveredTests; + int passed = p.PassedTests; + int failed = p.FailedTests; + int skipped = p.SkippedTests; + + if (!p.IsDiscovery) + { + // Use just ascii here, so we don't put too many restrictions on fonts needing to + // properly show unicode, or logs being saved in particular encoding. + Append('['); + SetColor(TerminalColor.DarkGreen); + Append('+'); + Append(passed.ToString(CultureInfo.CurrentCulture)); + ResetColor(); + + Append('/'); + + SetColor(TerminalColor.DarkRed); + Append('x'); + Append(failed.ToString(CultureInfo.CurrentCulture)); + ResetColor(); + + Append('/'); + + SetColor(TerminalColor.DarkYellow); + Append('?'); + Append(skipped.ToString(CultureInfo.CurrentCulture)); + ResetColor(); + Append(']'); + } + else + { + // Use just ascii here, so we don't put too many restrictions on fonts needing to + // properly show unicode, or logs being saved in particular encoding. + Append('['); + SetColor(TerminalColor.DarkMagenta); + Append('+'); + Append(discovered.ToString(CultureInfo.CurrentCulture)); + ResetColor(); + + Append(']'); + } + + Append(' '); + Append(p.AssemblyName); + + if (p.TargetFramework != null || p.Architecture != null) + { + Append(" ("); + if (p.TargetFramework != null) + { + Append(p.TargetFramework); + Append('|'); + } + + if (p.Architecture != null) + { + Append(p.Architecture); + } + + Append(')'); + } + + TestDetailState? activeTest = p.TestNodeResultsState?.GetRunningTasks(1).FirstOrDefault(); + if (!RoslynString.IsNullOrWhiteSpace(activeTest?.Text)) + { + Append(" - "); + Append(activeTest.Text); + Append(' '); + } + + Append(durationString); + + AppendLine(); + } + + // Do not render empty lines when there is nothing to show. + if (count > 0) + { + AppendLine(); + } + } + + public void ShowCursor() + { + // nop + } + + public void StartBusyIndicator() + { + // nop + } + + // TODO: Refactor NonAnsiTerminal and AnsiTerminal such that we don't need StartUpdate/StopUpdate. + // It's much better if we use lock C# keyword instead of manually calling Monitor.Enter/Exit + // Using lock also ensures we don't accidentally have `await`s in between that could cause Exit to be on a different thread. + public void StartUpdate() + { + if (_isBatching) + { + throw new InvalidOperationException(PlatformResources.ConsoleIsAlreadyInBatchingMode); + } + + bool lockTaken = false; + + // We store Console.Out in a field to make sure we will be doing + // the Monitor.Exit call on the same instance. + _batchingLock = System.Console.Out; + + // Note that we need to lock on System.Out for batching to work correctly. + // Consider the following scenario: + // 1. We call StartUpdate + // 2. We call a Write("A") + // 3. User calls Console.Write("B") from another thread. + // 4. We call a Write("C"). + // 5. We call StopUpdate. + // The expectation is that we see either ACB, or BAC, but not ABC. + // Basically, when doing batching, we want to ensure that everything we write is + // written continuously, without anything in-between. + // One option (and we used to do it), is that we append to a StringBuilder while batching + // Then at StopUpdate, we write the whole string at once. + // This works to some extent, but we cannot get it to work when SetColor kicks in. + // Console methods will internally lock on Console.Out, so we are locking on the same thing. + // This locking is the easiest way to get coloring to work correctly while preventing + // interleaving with user's calls to Console.Write methods. + // One extra note: + // It's very important to lock on Console.Out (the current Console.Out). + // Consider the following scenario: + // 1. SystemConsole captures the original Console.Out set by runtime. + // 2. Framework author sets his own Console.Out which wraps the original Console.Out. + // 3. Two threads are writing concurrently: + // - One thread is writing using Console.Write* APIs, which will use the Console.Out set by framework author. + // - The other thread is writing using NonAnsiTerminal. + // 4. **If** we lock the original Console.Out. The following may happen (subject to race) [NOT THE CURRENT CASE - imaginary situation if we lock on the original Console.Out]: + // - First thread enters the Console.Write, which will acquire the lock for the current Console.Out (set by framework author). + // - Second thread executes StartUpdate, and acquires the lock for the original Console.Out. + // - First thread continues in the Write implementation of the framework author, which tries to run Console.Write on the original Console.Out. + // - First thread can't make any progress, because the second thread is holding the lock already. + // - Second thread continues execution, and reaches into runtime code (ConsolePal.WriteFromConsoleStream - on Unix) which tries to acquire the lock for the current Console.Out (set by framework author). + // - (see https://github.com/dotnet/runtime/blob/8a9d492444f06df20fcc5dfdcf7a6395af18361f/src/libraries/System.Console/src/System/ConsolePal.Unix.cs#L963) + // - No thread can progress. + // - Basically, what happened is that the first thread acquires the lock for current Console.Out, then for the original Console.Out. + // - while the second thread acquires the lock for the original Console.Out, then for the current Console.Out. + // - That's a typical deadlock where two threads are acquiring two locks in reverse order. + // 5. By locking the *current* Console.Out, we avoid the situation described in 4. + Monitor.Enter(_batchingLock, ref lockTaken); + if (!lockTaken) + { + // Can this happen? :/ + throw new InvalidOperationException(); + } + + _isBatching = true; + } + + public void StopBusyIndicator() + { + // nop + } + + public void StopUpdate() + { + Monitor.Exit(_batchingLock!); + _batchingLock = null; + _isBatching = false; + } + + public abstract void SetColor(TerminalColor color); + + public abstract void ResetColor(); +} diff --git a/src/Platform/Microsoft.Testing.Platform/OutputDevice/Terminal/TerminalTestReporter.cs b/src/Platform/Microsoft.Testing.Platform/OutputDevice/Terminal/TerminalTestReporter.cs index 905fc50137..f128d74c42 100644 --- a/src/Platform/Microsoft.Testing.Platform/OutputDevice/Terminal/TerminalTestReporter.cs +++ b/src/Platform/Microsoft.Testing.Platform/OutputDevice/Terminal/TerminalTestReporter.cs @@ -3,6 +3,7 @@ using Microsoft.Testing.Platform.Helpers; using Microsoft.Testing.Platform.Resources; +using Microsoft.Testing.Platform.Services; namespace Microsoft.Testing.Platform.OutputDevice.Terminal; @@ -15,7 +16,7 @@ internal sealed partial class TerminalTestReporter : IDisposable /// /// The two directory separator characters to be passed to methods like . /// - private static readonly string[] NewLineStrings = { "\r\n", "\n" }; + private static readonly string[] NewLineStrings = ["\r\n", "\n"]; internal const string SingleIndentation = " "; @@ -35,15 +36,22 @@ internal event EventHandler OnProgressStopUpdate remove => _terminalWithProgress.OnProgressStopUpdate -= value; } - private readonly ConcurrentDictionary _assemblies = new(); + private readonly string _assembly; + private readonly string? _targetFramework; + private readonly string? _architecture; + private readonly ITestApplicationCancellationTokenSource _testApplicationCancellationTokenSource; - private readonly List _artifacts = new(); + private readonly List _artifacts = []; private readonly TerminalTestReporterOptions _options; private readonly TestProgressStateAwareTerminal _terminalWithProgress; + private readonly Lock _lock = new(); private readonly uint? _originalConsoleMode; + + private TestProgressState? _testProgressState; + private bool _isDiscovery; private DateTimeOffset? _testExecutionStartTime; @@ -51,7 +59,11 @@ internal event EventHandler OnProgressStopUpdate private int _buildErrorsCount; - private bool _wasCancelled; + private bool WasCancelled + { + get => field || _testApplicationCancellationTokenSource.CancellationToken.IsCancellationRequested; + set; + } private bool? _shouldShowPassedTests; @@ -60,8 +72,18 @@ internal event EventHandler OnProgressStopUpdate /// /// Initializes a new instance of the class with custom terminal and manual refresh for testing. /// - public TerminalTestReporter(IConsole console, TerminalTestReporterOptions options) - { + public TerminalTestReporter( + string assembly, + string? targetFramework, + string? architecture, + IConsole console, + ITestApplicationCancellationTokenSource testApplicationCancellationTokenSource, + TerminalTestReporterOptions options) + { + _assembly = assembly; + _targetFramework = targetFramework; + _architecture = architecture; + _testApplicationCancellationTokenSource = testApplicationCancellationTokenSource; _options = options; Func showProgress = _options.ShowProgress; @@ -77,12 +99,20 @@ public TerminalTestReporter(IConsole console, TerminalTestReporterOptions option } else { - // Autodetect. - (bool consoleAcceptsAnsiCodes, bool _, uint? originalConsoleMode) = NativeMethods.QueryIsScreenAndTryEnableAnsiColorCodes(); - _originalConsoleMode = originalConsoleMode; - terminalWithProgress = consoleAcceptsAnsiCodes || _options.ForceAnsi is true - ? new TestProgressStateAwareTerminal(new AnsiTerminal(console, _options.BaseDirectory), showProgress, writeProgressImmediatelyAfterOutput: true, updateEvery: ansiUpdateCadenceInMs) - : new TestProgressStateAwareTerminal(new NonAnsiTerminal(console), showProgress, writeProgressImmediatelyAfterOutput: false, updateEvery: nonAnsiUpdateCadenceInMs); + if (_options.UseCIAnsi) + { + // We are told externally that we are in CI, use simplified ANSI mode. + terminalWithProgress = new TestProgressStateAwareTerminal(new SimpleAnsiTerminal(console), showProgress, writeProgressImmediatelyAfterOutput: true, updateEvery: nonAnsiUpdateCadenceInMs); + } + else + { + // We are not in CI, or in CI non-compatible with simple ANSI, autodetect terminal capabilities + (bool consoleAcceptsAnsiCodes, bool _, uint? originalConsoleMode) = NativeMethods.QueryIsScreenAndTryEnableAnsiColorCodes(); + _originalConsoleMode = originalConsoleMode; + terminalWithProgress = consoleAcceptsAnsiCodes || _options.ForceAnsi is true + ? new TestProgressStateAwareTerminal(new AnsiTerminal(console), showProgress, writeProgressImmediatelyAfterOutput: true, updateEvery: ansiUpdateCadenceInMs) + : new TestProgressStateAwareTerminal(new NonAnsiTerminal(console), showProgress, writeProgressImmediatelyAfterOutput: false, updateEvery: nonAnsiUpdateCadenceInMs); + } } _terminalWithProgress = terminalWithProgress; @@ -95,34 +125,30 @@ public void TestExecutionStarted(DateTimeOffset testStartTime, int workerCount, _terminalWithProgress.StartShowingProgress(workerCount); } - public void AssemblyRunStarted(string assembly, string? targetFramework, string? architecture) + public void AssemblyRunStarted() + => GetOrAddAssemblyRun(); + + private TestProgressState GetOrAddAssemblyRun() { - if (_options.ShowAssembly && _options.ShowAssemblyStartAndComplete) + if (_testProgressState is not null) { - _terminalWithProgress.WriteToTerminal(terminal => - { - terminal.Append(_isDiscovery ? PlatformResources.DiscoveringTestsFrom : PlatformResources.RunningTestsFrom); - terminal.Append(' '); - AppendAssemblyLinkTargetFrameworkAndArchitecture(terminal, assembly, targetFramework, architecture); - terminal.AppendLine(); - }); + return _testProgressState; } - GetOrAddAssemblyRun(assembly, targetFramework, architecture); - } - - private TestProgressState GetOrAddAssemblyRun(string assembly, string? targetFramework, string? architecture) - { - string key = $"{assembly}|{targetFramework}|{architecture}"; - return _assemblies.GetOrAdd(key, _ => + lock (_lock) { + if (_testProgressState is not null) + { + return _testProgressState; + } + IStopwatch sw = CreateStopwatch(); - var assemblyRun = new TestProgressState(Interlocked.Increment(ref _counter), assembly, targetFramework, architecture, sw); + var assemblyRun = new TestProgressState(Interlocked.Increment(ref _counter), _assembly, _targetFramework, _architecture, sw, _isDiscovery); int slotIndex = _terminalWithProgress.AddWorker(assemblyRun); assemblyRun.SlotIndex = slotIndex; - + _testProgressState = assemblyRun; return assemblyRun; - }); + } } public void TestExecutionCompleted(DateTimeOffset endTime) @@ -133,7 +159,11 @@ public void TestExecutionCompleted(DateTimeOffset endTime) _terminalWithProgress.WriteToTerminal(_isDiscovery ? AppendTestDiscoverySummary : AppendTestRunSummary); NativeMethods.RestoreConsoleMode(_originalConsoleMode); - _assemblies.Clear(); + + // This is relevant for HotReload scenarios. We want the next test sessions to start + // on a new TestProgressState + _testProgressState = null; + _buildErrorsCount = 0; _testExecutionStartTime = null; _testExecutionEndTime = null; @@ -171,20 +201,20 @@ private void AppendTestRunSummary(ITerminal terminal) terminal.AppendLine(); - int totalTests = _assemblies.Values.Sum(a => a.TotalTests); - int totalFailedTests = _assemblies.Values.Sum(a => a.FailedTests); - int totalSkippedTests = _assemblies.Values.Sum(a => a.SkippedTests); + int totalTests = _testProgressState?.TotalTests ?? 0; + int totalFailedTests = _testProgressState?.FailedTests ?? 0; + int totalSkippedTests = _testProgressState?.SkippedTests ?? 0; bool notEnoughTests = totalTests < _options.MinimumExpectedTests; bool allTestsWereSkipped = totalTests == 0 || totalTests == totalSkippedTests; bool anyTestFailed = totalFailedTests > 0; - bool runFailed = anyTestFailed || notEnoughTests || allTestsWereSkipped || _wasCancelled; - terminal.SetColor(runFailed ? TerminalColor.Red : TerminalColor.Green); + bool runFailed = anyTestFailed || notEnoughTests || allTestsWereSkipped || WasCancelled; + terminal.SetColor(runFailed ? TerminalColor.DarkRed : TerminalColor.DarkGreen); terminal.Append(PlatformResources.TestRunSummary); terminal.Append(' '); - if (_wasCancelled) + if (WasCancelled) { terminal.Append(PlatformResources.Aborted); } @@ -198,57 +228,41 @@ private void AppendTestRunSummary(ITerminal terminal) } else if (anyTestFailed) { - terminal.Append(string.Format(CultureInfo.CurrentCulture, "{0}!", PlatformResources.Failed)); + terminal.Append($"{PlatformResources.Failed}!"); } else { - terminal.Append(string.Format(CultureInfo.CurrentCulture, "{0}!", PlatformResources.Passed)); + terminal.Append($"{PlatformResources.Passed}!"); } - if (!_options.ShowAssembly && _assemblies.Count == 1) - { - TestProgressState testProgressState = _assemblies.Values.Single(); - terminal.SetColor(TerminalColor.DarkGray); - terminal.Append(" - "); - terminal.ResetColor(); - AppendAssemblyLinkTargetFrameworkAndArchitecture(terminal, testProgressState.Assembly, testProgressState.TargetFramework, testProgressState.Architecture); - } + terminal.SetColor(TerminalColor.DarkGray); + terminal.Append(" - "); + terminal.ResetColor(); + AppendAssemblyLinkTargetFrameworkAndArchitecture(terminal); terminal.AppendLine(); - if (_options.ShowAssembly && _assemblies.Count > 1) - { - foreach (TestProgressState assemblyRun in _assemblies.Values) - { - terminal.Append(SingleIndentation); - AppendAssemblySummary(assemblyRun, terminal); - terminal.AppendLine(); - } - - terminal.AppendLine(); - } - - int total = _assemblies.Values.Sum(t => t.TotalTests); - int failed = _assemblies.Values.Sum(t => t.FailedTests); - int passed = _assemblies.Values.Sum(t => t.PassedTests); - int skipped = _assemblies.Values.Sum(t => t.SkippedTests); + int total = _testProgressState?.TotalTests ?? 0; + int failed = _testProgressState?.FailedTests ?? 0; + int passed = _testProgressState?.PassedTests ?? 0; + int skipped = _testProgressState?.SkippedTests ?? 0; TimeSpan runDuration = _testExecutionStartTime != null && _testExecutionEndTime != null ? (_testExecutionEndTime - _testExecutionStartTime).Value : TimeSpan.Zero; bool colorizeFailed = failed > 0; bool colorizePassed = passed > 0 && _buildErrorsCount == 0 && failed == 0; bool colorizeSkipped = skipped > 0 && skipped == total && _buildErrorsCount == 0 && failed == 0; - string totalText = $"{SingleIndentation}total: {total}"; - string failedText = $"{SingleIndentation}failed: {failed}"; - string passedText = $"{SingleIndentation}succeeded: {passed}"; - string skippedText = $"{SingleIndentation}skipped: {skipped}"; - string durationText = $"{SingleIndentation}duration: "; + string totalText = $"{SingleIndentation}{PlatformResources.TotalLowercase}: {total}"; + string failedText = $"{SingleIndentation}{PlatformResources.FailedLowercase}: {failed}"; + string passedText = $"{SingleIndentation}{PlatformResources.SucceededLowercase}: {passed}"; + string skippedText = $"{SingleIndentation}{PlatformResources.SkippedLowercase}: {skipped}"; + string durationText = $"{SingleIndentation}{PlatformResources.DurationLowercase}: "; terminal.ResetColor(); terminal.AppendLine(totalText); if (colorizeFailed) { - terminal.SetColor(TerminalColor.Red); + terminal.SetColor(TerminalColor.DarkRed); } terminal.AppendLine(failedText); @@ -260,7 +274,7 @@ private void AppendTestRunSummary(ITerminal terminal) if (colorizePassed) { - terminal.SetColor(TerminalColor.Green); + terminal.SetColor(TerminalColor.DarkGreen); } terminal.AppendLine(passedText); @@ -272,7 +286,7 @@ private void AppendTestRunSummary(ITerminal terminal) if (colorizeSkipped) { - terminal.SetColor(TerminalColor.Yellow); + terminal.SetColor(TerminalColor.DarkYellow); } terminal.AppendLine(skippedText); @@ -287,43 +301,7 @@ private void AppendTestRunSummary(ITerminal terminal) terminal.AppendLine(); } - /// - /// Print a build result summary to the output. - /// - private static void AppendAssemblyResult(ITerminal terminal, bool succeeded, int countErrors, int countWarnings) - { - if (!succeeded) - { - terminal.SetColor(TerminalColor.Red); - // If the build failed, we print one of three red strings. - string text = (countErrors > 0, countWarnings > 0) switch - { - (true, true) => string.Format(CultureInfo.CurrentCulture, PlatformResources.FailedWithErrorsAndWarnings, countErrors, countWarnings), - (true, _) => string.Format(CultureInfo.CurrentCulture, PlatformResources.FailedWithErrors, countErrors), - (false, true) => string.Format(CultureInfo.CurrentCulture, PlatformResources.FailedWithWarnings, countWarnings), - _ => PlatformResources.FailedLowercase, - }; - terminal.Append(text); - terminal.ResetColor(); - } - else if (countWarnings > 0) - { - terminal.SetColor(TerminalColor.Yellow); - terminal.Append($"succeeded with {countWarnings} warning(s)"); - terminal.ResetColor(); - } - else - { - terminal.SetColor(TerminalColor.Green); - terminal.Append(PlatformResources.PassedLowercase); - terminal.ResetColor(); - } - } - internal void TestCompleted( - string assembly, - string? targetFramework, - string? architecture, string testNodeUid, string displayName, TestOutcome outcome, @@ -338,9 +316,6 @@ internal void TestCompleted( { FlatException[] flatExceptions = ExceptionFlattener.Flatten(errorMessage, exception); TestCompleted( - assembly, - targetFramework, - architecture, testNodeUid, displayName, outcome, @@ -354,9 +329,6 @@ internal void TestCompleted( } private void TestCompleted( - string assembly, - string? targetFramework, - string? architecture, string testNodeUid, string displayName, TestOutcome outcome, @@ -368,7 +340,12 @@ private void TestCompleted( string? standardOutput, string? errorOutput) { - TestProgressState asm = _assemblies[$"{assembly}|{targetFramework}|{architecture}"]; + if (_testProgressState is null) + { + throw ApplicationStateGuard.Unreachable(); + } + + TestProgressState asm = _testProgressState; if (_options.ShowActiveTests) { @@ -399,9 +376,6 @@ private void TestCompleted( { _terminalWithProgress.WriteToTerminal(terminal => RenderTestCompleted( terminal, - assembly, - targetFramework, - architecture, displayName, outcome, duration, @@ -422,9 +396,6 @@ private bool GetShowPassedTests() private void RenderTestCompleted( ITerminal terminal, - string assembly, - string? targetFramework, - string? architecture, string displayName, TestOutcome outcome, TimeSpan duration, @@ -442,9 +413,9 @@ private void RenderTestCompleted( TerminalColor color = outcome switch { - TestOutcome.Error or TestOutcome.Fail or TestOutcome.Canceled or TestOutcome.Timeout => TerminalColor.Red, - TestOutcome.Skipped => TerminalColor.Yellow, - TestOutcome.Passed => TerminalColor.Green, + TestOutcome.Error or TestOutcome.Fail or TestOutcome.Canceled or TestOutcome.Timeout => TerminalColor.DarkRed, + TestOutcome.Skipped => TerminalColor.DarkYellow, + TestOutcome.Passed => TerminalColor.DarkGreen, _ => throw new NotSupportedException(), }; string outcomeText = outcome switch @@ -460,18 +431,10 @@ private void RenderTestCompleted( terminal.Append(outcomeText); terminal.ResetColor(); terminal.Append(' '); - terminal.Append(displayName); + terminal.Append(MakeControlCharactersVisible(displayName, true)); terminal.SetColor(TerminalColor.DarkGray); terminal.Append(' '); AppendLongDuration(terminal, duration); - if (_options.ShowAssembly) - { - terminal.AppendLine(); - terminal.Append(SingleIndentation); - terminal.Append(PlatformResources.FromFile); - terminal.Append(' '); - AppendAssemblyLinkTargetFrameworkAndArchitecture(terminal, assembly, targetFramework, architecture); - } terminal.AppendLine(); @@ -492,7 +455,7 @@ private static void FormatInnerExceptions(ITerminal terminal, FlatException[] ex for (int i = 1; i < exceptions.Length; i++) { - terminal.SetColor(TerminalColor.Red); + terminal.SetColor(TerminalColor.DarkRed); terminal.Append(SingleIndentation); terminal.Append("--->"); FormatErrorMessage(terminal, exceptions, TestOutcome.Error, i); @@ -510,7 +473,7 @@ private static void FormatErrorMessage(ITerminal terminal, FlatException[] excep return; } - terminal.SetColor(TerminalColor.Red); + terminal.SetColor(TerminalColor.DarkRed); if (firstStackTrace is null) { @@ -539,7 +502,7 @@ private static void FormatExpectedAndActual(ITerminal terminal, string? expected return; } - terminal.SetColor(TerminalColor.Red); + terminal.SetColor(TerminalColor.DarkRed); terminal.Append(SingleIndentation); terminal.AppendLine(PlatformResources.Expected); AppendIndentedLine(terminal, expected, DoubleIndentation); @@ -578,76 +541,83 @@ private static void FormatStandardAndErrorOutput(ITerminal terminal, string? sta terminal.SetColor(TerminalColor.DarkGray); terminal.Append(SingleIndentation); terminal.AppendLine(PlatformResources.StandardOutput); - string? standardOutputWithoutSpecialChars = NormalizeSpecialCharacters(standardOutput); + string? standardOutputWithoutSpecialChars = MakeControlCharactersVisible(standardOutput, normalizeWhitespaceCharacters: false); AppendIndentedLine(terminal, standardOutputWithoutSpecialChars, DoubleIndentation); terminal.Append(SingleIndentation); terminal.AppendLine(PlatformResources.StandardError); - string? standardErrorWithoutSpecialChars = NormalizeSpecialCharacters(standardError); + string? standardErrorWithoutSpecialChars = MakeControlCharactersVisible(standardError, normalizeWhitespaceCharacters: false); AppendIndentedLine(terminal, standardErrorWithoutSpecialChars, DoubleIndentation); terminal.ResetColor(); } - private static void AppendAssemblyLinkTargetFrameworkAndArchitecture(ITerminal terminal, string assembly, string? targetFramework, string? architecture) + private void AppendAssemblyLinkTargetFrameworkAndArchitecture(ITerminal terminal) { - terminal.AppendLink(assembly, lineNumber: null); - if (targetFramework != null || architecture != null) + terminal.AppendLink(_assembly, lineNumber: null); + if (_targetFramework == null && _architecture == null) { - terminal.Append(" ("); - if (targetFramework != null) - { - terminal.Append(targetFramework); - terminal.Append('|'); - } + return; + } - if (architecture != null) + terminal.Append(" ("); + if (_targetFramework != null) + { + terminal.Append(_targetFramework); + if (_architecture != null) { - terminal.Append(architecture); + terminal.Append('|'); } + } - terminal.Append(')'); + if (_architecture != null) + { + terminal.Append(_architecture); } + + terminal.Append(')'); } internal /* for testing */ static void AppendStackFrame(ITerminal terminal, string stackTraceLine) { terminal.Append(DoubleIndentation); Match match = StackTraceHelper.GetFrameRegex().Match(stackTraceLine); - if (match.Success) + if (!match.Success) { - bool weHaveFilePathAndCodeLine = !RoslynString.IsNullOrWhiteSpace(match.Groups["code"].Value); - terminal.Append(PlatformResources.StackFrameAt); - terminal.Append(' '); - - if (weHaveFilePathAndCodeLine) - { - terminal.Append(match.Groups["code"].Value); - } - else - { - terminal.Append(match.Groups["code1"].Value); - } + terminal.AppendLine(stackTraceLine); + return; + } - if (weHaveFilePathAndCodeLine) - { - terminal.Append(' '); - terminal.Append(PlatformResources.StackFrameIn); - terminal.Append(' '); - if (!RoslynString.IsNullOrWhiteSpace(match.Groups["file"].Value)) - { - int line = int.TryParse(match.Groups["line"].Value, out int value) ? value : 0; - terminal.AppendLink(match.Groups["file"].Value, line); + bool weHaveFilePathAndCodeLine = !RoslynString.IsNullOrWhiteSpace(match.Groups["code"].Value); + terminal.Append(PlatformResources.StackFrameAt); + terminal.Append(' '); - // AppendLink finishes by resetting color - terminal.SetColor(TerminalColor.DarkGray); - } - } + if (weHaveFilePathAndCodeLine) + { + terminal.Append(match.Groups["code"].Value); + } + else + { + terminal.Append(match.Groups["code1"].Value); + } + if (!weHaveFilePathAndCodeLine) + { terminal.AppendLine(); + return; } - else + + terminal.Append(' '); + terminal.Append(PlatformResources.StackFrameIn); + terminal.Append(' '); + if (!RoslynString.IsNullOrWhiteSpace(match.Groups["file"].Value)) { - terminal.AppendLine(stackTraceLine); + int line = int.TryParse(match.Groups["line"].Value, out int value) ? value : 0; + terminal.AppendLink(match.Groups["file"].Value, line); + + // AppendLink finishes by resetting color + terminal.SetColor(TerminalColor.DarkGray); } + + terminal.AppendLine(); } private static void AppendIndentedLine(ITerminal terminal, string? message, string indent) @@ -678,63 +648,108 @@ private static void AppendIndentedLine(ITerminal terminal, string? message, stri } } - internal void AssemblyRunCompleted(string assembly, string? targetFramework, string? architecture, - // These parameters are useful only for "remote" runs in dotnet test, where we are reporting on multiple processes. - // In single process run, like with testing platform .exe we report these via messages, and run exit. - int? exitCode, string? outputData, string? errorData) + internal void AssemblyRunCompleted() { - TestProgressState assemblyRun = GetOrAddAssemblyRun(assembly, targetFramework, architecture); + TestProgressState assemblyRun = GetOrAddAssemblyRun(); assemblyRun.Stopwatch.Stop(); _terminalWithProgress.RemoveWorker(assemblyRun.SlotIndex); + } - if (!_isDiscovery && _options.ShowAssembly && _options.ShowAssemblyStartAndComplete) + // SearchValues for efficient detection of control characters +#if NET8_0_OR_GREATER + private static readonly System.Buffers.SearchValues AllControlChars = CreateControlCharSearchValues(includeWhitespace: true); + private static readonly System.Buffers.SearchValues NonWhitespaceControlChars = CreateControlCharSearchValues(includeWhitespace: false); + + private static System.Buffers.SearchValues CreateControlCharSearchValues(bool includeWhitespace) + { + var controlChars = new List(); + for (char c = '\0'; c <= '\u00FF'; c++) // Check first 256 characters for performance { - _terminalWithProgress.WriteToTerminal(terminal => AppendAssemblySummary(assemblyRun, terminal)); + if (char.IsControl(c)) + { + if (includeWhitespace || (c != '\t' && c != '\n' && c != '\r')) + { + controlChars.Add(c); + } + } } - if (exitCode is null or 0) + return System.Buffers.SearchValues.Create(controlChars.ToArray()); + } +#else + private static readonly char[] AllControlChars = CreateControlCharArray(includeWhitespace: true); + private static readonly char[] NonWhitespaceControlChars = CreateControlCharArray(includeWhitespace: false); + + private static char[] CreateControlCharArray(bool includeWhitespace) + { + var controlChars = new List(); + for (char c = '\0'; c <= '\u00FF'; c++) // Check first 256 characters for performance { - // Report nothing, we don't want to report on success, because then we will also report on test-discovery etc. - return; + if (char.IsControl(c)) + { + if (includeWhitespace || (c != '\t' && c != '\n' && c != '\r')) + { + controlChars.Add(c); + } + } } - _terminalWithProgress.WriteToTerminal(terminal => - { - AppendExecutableSummary(terminal, exitCode, outputData, errorData); - terminal.AppendLine(); - }); + return [.. controlChars]; } +#endif - private static void AppendExecutableSummary(ITerminal terminal, int? exitCode, string? outputData, string? errorData) + [return: NotNullIfNotNull(nameof(text))] + private static string? MakeControlCharactersVisible(string? text, bool normalizeWhitespaceCharacters) { - terminal.AppendLine(); - terminal.Append(PlatformResources.ExitCode); - terminal.Append(": "); - terminal.AppendLine(exitCode?.ToString(CultureInfo.CurrentCulture) ?? ""); - terminal.Append(PlatformResources.StandardOutput); - terminal.AppendLine(":"); - terminal.AppendLine(RoslynString.IsNullOrWhiteSpace(outputData) ? string.Empty : outputData); - terminal.Append(PlatformResources.StandardError); - terminal.AppendLine(":"); - terminal.AppendLine(RoslynString.IsNullOrWhiteSpace(errorData) ? string.Empty : errorData); - } + if (text is null) + { + return null; + } - private static string? NormalizeSpecialCharacters(string? text) - => text?.Replace('\0', '\x2400') - // escape char - .Replace('\x001b', '\x241b'); +#if NET8_0_OR_GREATER + // Use SearchValues to efficiently check if we need to do any work + System.Buffers.SearchValues searchValues = normalizeWhitespaceCharacters ? AllControlChars : NonWhitespaceControlChars; + if (text.AsSpan().IndexOfAny(searchValues) == -1) + { + return text; // No control characters found, return original string + } +#else + // Use IndexOfAny to check if we need to do any work + char[] searchChars = normalizeWhitespaceCharacters ? AllControlChars : NonWhitespaceControlChars; + if (text.IndexOfAny(searchChars) == -1) + { + return text; // No control characters found, return original string + } +#endif - private static void AppendAssemblySummary(TestProgressState assemblyRun, ITerminal terminal) - { - int failedTests = assemblyRun.FailedTests; - int warnings = 0; + // Pre-allocate StringBuilder with known capacity + var sb = new StringBuilder(text.Length); - AppendAssemblyLinkTargetFrameworkAndArchitecture(terminal, assemblyRun.Assembly, assemblyRun.TargetFramework, assemblyRun.Architecture); - terminal.Append(' '); - AppendAssemblyResult(terminal, assemblyRun.FailedTests == 0, failedTests, warnings); - terminal.Append(' '); - AppendLongDuration(terminal, assemblyRun.Stopwatch.Elapsed); + foreach (char c in text) + { + if (char.IsControl(c)) + { + // Skip normalization for whitespace characters when not requested + if (!normalizeWhitespaceCharacters && (c == '\t' || c == '\n' || c == '\r')) + { + sb.Append(c); + } + else + { + // Convert to Unicode control picture using bit manipulation (0x2400 + char value) + // For C0 control characters (0x00-0x1F), this produces proper Unicode control pictures (U+2400-U+241F) + // For other control characters, this produces printable characters that won't break console formatting + sb.Append((char)(0x2400 + c)); + } + } + else + { + sb.Append(c); + } + } + + return sb.ToString(); } /// @@ -757,15 +772,15 @@ private static void AppendLongDuration(ITerminal terminal, TimeSpan duration, bo public void Dispose() => _terminalWithProgress.Dispose(); - public void ArtifactAdded(bool outOfProcess, string? assembly, string? targetFramework, string? architecture, string? testName, string path) - => _artifacts.Add(new TestRunArtifact(outOfProcess, assembly, targetFramework, architecture, testName, path)); + public void ArtifactAdded(bool outOfProcess, string? testName, string path) + => _artifacts.Add(new TestRunArtifact(outOfProcess, testName, path)); /// /// Let the user know that cancellation was triggered. /// public void StartCancelling() { - _wasCancelled = true; + WasCancelled = true; _terminalWithProgress.WriteToTerminal(terminal => { terminal.AppendLine(); @@ -774,34 +789,34 @@ public void StartCancelling() }); } - internal void WriteErrorMessage(string assembly, string? targetFramework, string? architecture, string text, int? padding) + internal void WriteErrorMessage(string text, int? padding) { - TestProgressState asm = GetOrAddAssemblyRun(assembly, targetFramework, architecture); + TestProgressState asm = GetOrAddAssemblyRun(); asm.AddError(text); - - _terminalWithProgress.WriteToTerminal(terminal => - { - terminal.SetColor(TerminalColor.Red); - if (padding == null) - { - terminal.AppendLine(text); - } - else - { - AppendIndentedLine(terminal, text, new string(' ', padding.Value)); - } - - terminal.ResetColor(); - }); + WriteMessage(text, TerminalColor.DarkRed, padding); } - internal void WriteWarningMessage(string assembly, string? targetFramework, string? architecture, string text, int? padding) + internal void WriteWarningMessage(string text, int? padding) { - TestProgressState asm = GetOrAddAssemblyRun(assembly, targetFramework, architecture); + TestProgressState asm = GetOrAddAssemblyRun(); asm.AddWarning(text); - _terminalWithProgress.WriteToTerminal(terminal => + WriteMessage(text, TerminalColor.DarkYellow, padding); + } + + internal void WriteErrorMessage(Exception exception) + => WriteErrorMessage(exception.ToString(), padding: null); + + public void WriteMessage(string text, SystemConsoleColor? color = null, int? padding = null) + => WriteMessage(text, color is not null ? ToTerminalColor(color.ConsoleColor) : null, padding); + + private void WriteMessage(string text, TerminalColor? color = null, int? padding = null) + => _terminalWithProgress.WriteToTerminal(terminal => { - terminal.SetColor(TerminalColor.Yellow); + if (color.HasValue) + { + terminal.SetColor(color.Value); + } + if (padding == null) { terminal.AppendLine(text); @@ -811,130 +826,71 @@ internal void WriteWarningMessage(string assembly, string? targetFramework, stri AppendIndentedLine(terminal, text, new string(' ', padding.Value)); } - terminal.ResetColor(); + if (color.HasValue) + { + terminal.ResetColor(); + } }); - } - - internal void WriteErrorMessage(string assembly, string? targetFramework, string? architecture, Exception exception) - => WriteErrorMessage(assembly, targetFramework, architecture, exception.ToString(), padding: null); - public void WriteMessage(string text, SystemConsoleColor? color = null, int? padding = null) + internal void TestDiscovered(string displayName) { - if (color != null) + if (_testProgressState is null) { - _terminalWithProgress.WriteToTerminal(terminal => - { - terminal.SetColor(ToTerminalColor(color.ConsoleColor)); - if (padding == null) - { - terminal.AppendLine(text); - } - else - { - AppendIndentedLine(terminal, text, new string(' ', padding.Value)); - } - - terminal.ResetColor(); - }); - } - else - { - _terminalWithProgress.WriteToTerminal(terminal => - { - if (padding == null) - { - terminal.AppendLine(text); - } - else - { - AppendIndentedLine(terminal, text, new string(' ', padding.Value)); - } - }); + throw ApplicationStateGuard.Unreachable(); } - } - internal void TestDiscovered( - string assembly, - string? targetFramework, - string? architecture, - string? displayName, - string? uid) - { - TestProgressState asm = _assemblies[$"{assembly}|{targetFramework}|{architecture}"]; + TestProgressState asm = _testProgressState; + asm.DiscoveredTests++; if (_isDiscovery) { - // TODO: add mode for discovered tests to the progress bar, to get rid of the hack here that allows updating the - // progress, but also breaks the total counts if not done only in discovery. - asm.PassedTests++; + // In discovery mode we count discovered tests, + // but in execution mode the completion of test will increase the total tests count. asm.TotalTests++; } - asm.DiscoveredTests.Add(new(displayName, uid)); + asm.DiscoveredTestDisplayNames.Add(MakeControlCharactersVisible(displayName, true)); _terminalWithProgress.UpdateWorker(asm.SlotIndex); } public void AppendTestDiscoverySummary(ITerminal terminal) { + TestProgressState? assembly = _testProgressState; terminal.AppendLine(); - var assemblies = _assemblies.Select(asm => asm.Value).OrderBy(a => a.Assembly).Where(a => a is not null).ToList(); + int totalTests = assembly?.TotalTests ?? 0; + bool runFailed = WasCancelled || totalTests < 1; - int totalTests = _assemblies.Values.Sum(a => a.TotalTests); - bool runFailed = _wasCancelled; - - foreach (TestProgressState assembly in assemblies) + if (assembly is not null) { - if (_options.ShowAssembly) - { - terminal.Append(string.Format(CultureInfo.CurrentCulture, PlatformResources.DiscoveredTestsInAssembly, assembly.DiscoveredTests.Count)); - terminal.Append(" - "); - AppendAssemblyLinkTargetFrameworkAndArchitecture(terminal, assembly.Assembly, assembly.TargetFramework, assembly.Architecture); - terminal.AppendLine(); - } - - foreach ((string? displayName, string? uid) in assembly.DiscoveredTests) + foreach (string displayName in assembly.DiscoveredTestDisplayNames) { - if (displayName is not null) - { - terminal.Append(SingleIndentation); - terminal.AppendLine(displayName); - } + terminal.Append(SingleIndentation); + terminal.AppendLine(displayName); } - - terminal.AppendLine(); } - terminal.SetColor(runFailed ? TerminalColor.Red : TerminalColor.Green); - if (assemblies.Count <= 1) - { - terminal.Append(string.Format(CultureInfo.CurrentCulture, PlatformResources.TestDiscoverySummarySingular, totalTests)); + terminal.AppendLine(); - if (!_options.ShowAssembly && _assemblies.Count == 1) - { - TestProgressState testProgressState = _assemblies.Values.Single(); - terminal.SetColor(TerminalColor.DarkGray); - terminal.Append(" - "); - terminal.ResetColor(); - AppendAssemblyLinkTargetFrameworkAndArchitecture(terminal, testProgressState.Assembly, testProgressState.TargetFramework, testProgressState.Architecture); - } - } - else - { - terminal.Append(string.Format(CultureInfo.CurrentCulture, PlatformResources.TestDiscoverySummary, totalTests, assemblies.Count)); - } + terminal.SetColor(runFailed ? TerminalColor.DarkRed : TerminalColor.DarkGreen); + terminal.Append(string.Format(CultureInfo.CurrentCulture, PlatformResources.TestDiscoverySummarySingular, totalTests)); + + terminal.SetColor(TerminalColor.DarkGray); + terminal.Append(" - "); + terminal.ResetColor(); + AppendAssemblyLinkTargetFrameworkAndArchitecture(terminal); terminal.ResetColor(); terminal.AppendLine(); - if (_wasCancelled) + if (WasCancelled) { terminal.Append(PlatformResources.Aborted); terminal.AppendLine(); } - string durationText = $"{SingleIndentation}duration: "; + string durationText = $"{SingleIndentation}{PlatformResources.DurationLowercase}: "; TimeSpan runDuration = _testExecutionStartTime != null && _testExecutionEndTime != null ? (_testExecutionEndTime - _testExecutionStartTime).Value : TimeSpan.Zero; terminal.Append(durationText); AppendLongDuration(terminal, runDuration, wrapInParentheses: false, colorize: false); @@ -964,19 +920,20 @@ private static TerminalColor ToTerminalColor(ConsoleColor consoleColor) }; public void TestInProgress( - string assembly, - string? targetFramework, - string? architecture, string testNodeUid, string displayName) { - TestProgressState asm = _assemblies[$"{assembly}|{targetFramework}|{architecture}"]; + if (_testProgressState is null) + { + throw ApplicationStateGuard.Unreachable(); + } + TestProgressState asm = _testProgressState; if (_options.ShowActiveTests) { asm.TestNodeResultsState ??= new(Interlocked.Increment(ref _counter)); asm.TestNodeResultsState.AddRunningTestNode( - Interlocked.Increment(ref _counter), testNodeUid, displayName, CreateStopwatch()); + Interlocked.Increment(ref _counter), testNodeUid, MakeControlCharactersVisible(displayName, true), CreateStopwatch()); } _terminalWithProgress.UpdateWorker(asm.SlotIndex); diff --git a/src/Platform/Microsoft.Testing.Platform/OutputDevice/Terminal/TerminalTestReporterCommandLineOptionsProvider.cs b/src/Platform/Microsoft.Testing.Platform/OutputDevice/Terminal/TerminalTestReporterCommandLineOptionsProvider.cs index 97e965b324..5de9570121 100644 --- a/src/Platform/Microsoft.Testing.Platform/OutputDevice/Terminal/TerminalTestReporterCommandLineOptionsProvider.cs +++ b/src/Platform/Microsoft.Testing.Platform/OutputDevice/Terminal/TerminalTestReporterCommandLineOptionsProvider.cs @@ -18,10 +18,10 @@ internal sealed class TerminalTestReporterCommandLineOptionsProvider : ICommandL public const string OutputOptionDetailedArgument = "detailed"; /// - public string Uid { get; } = nameof(TerminalTestReporterCommandLineOptionsProvider); + public string Uid => nameof(TerminalTestReporterCommandLineOptionsProvider); /// - public string Version { get; } = AppVersion.DefaultSemVer; + public string Version => AppVersion.DefaultSemVer; /// public string DisplayName { get; } = PlatformResources.TerminalTestReporterDisplayName; diff --git a/src/Platform/Microsoft.Testing.Platform/OutputDevice/Terminal/TerminalTestReporterOptions.cs b/src/Platform/Microsoft.Testing.Platform/OutputDevice/Terminal/TerminalTestReporterOptions.cs index 478453758d..b738158389 100644 --- a/src/Platform/Microsoft.Testing.Platform/OutputDevice/Terminal/TerminalTestReporterOptions.cs +++ b/src/Platform/Microsoft.Testing.Platform/OutputDevice/Terminal/TerminalTestReporterOptions.cs @@ -5,26 +5,11 @@ namespace Microsoft.Testing.Platform.OutputDevice.Terminal; internal sealed class TerminalTestReporterOptions { - /// - /// Gets path to which all other paths in output should be relative. - /// - public string? BaseDirectory { get; init; } - /// /// Gets a value indicating whether we should show passed tests. /// public Func ShowPassedTests { get; init; } = () => true; - /// - /// Gets a value indicating whether we should show information about which assembly is the source of the data on screen. Turn this off when running directly from an exe to reduce noise, because the path will always be the same. - /// - public bool ShowAssembly { get; init; } - - /// - /// Gets a value indicating whether we should show information about which assembly started or completed. Turn this off when running directly from an exe to reduce noise, because the path will always be the same. - /// - public bool ShowAssemblyStartAndComplete { get; init; } - /// /// Gets minimum amount of tests to run. /// @@ -47,6 +32,12 @@ internal sealed class TerminalTestReporterOptions /// public bool UseAnsi { get; init; } + /// + /// Gets a value indicating whether we are running in compatible CI, and should use simplified ANSI renderer, which colors output, but does not move cursor. + /// Setting to false will disable this option. + /// + public bool UseCIAnsi { get; init; } + /// /// Gets a value indicating whether we should force ANSI escape codes. When true the ANSI is used without auto-detecting capabilities of the console. This is needed only for testing. /// diff --git a/src/Platform/Microsoft.Testing.Platform/OutputDevice/Terminal/TestNodeResultsState.cs b/src/Platform/Microsoft.Testing.Platform/OutputDevice/Terminal/TestNodeResultsState.cs index db574dfa97..f14fecc7aa 100644 --- a/src/Platform/Microsoft.Testing.Platform/OutputDevice/Terminal/TestNodeResultsState.cs +++ b/src/Platform/Microsoft.Testing.Platform/OutputDevice/Terminal/TestNodeResultsState.cs @@ -45,7 +45,7 @@ public IEnumerable GetRunningTasks(int maxCount) ? string.Format(CultureInfo.CurrentCulture, PlatformResources.ActiveTestsRunning_FullTestsCount, sortedDetails.Count) // If itemsToTake is larger, then we show the project summary, active tests, and the number of active tests that are not shown. : $"... {string.Format(CultureInfo.CurrentCulture, PlatformResources.ActiveTestsRunning_MoreTestsCount, sortedDetails.Count - itemsToTake)}"; - sortedDetails = sortedDetails.Take(itemsToTake).ToList(); + sortedDetails = [.. sortedDetails.Take(itemsToTake)]; } foreach (TestDetailState? detail in sortedDetails) diff --git a/src/Platform/Microsoft.Testing.Platform/OutputDevice/Terminal/TestProgressState.cs b/src/Platform/Microsoft.Testing.Platform/OutputDevice/Terminal/TestProgressState.cs index afc5d8d7cf..489654cc0e 100644 --- a/src/Platform/Microsoft.Testing.Platform/OutputDevice/Terminal/TestProgressState.cs +++ b/src/Platform/Microsoft.Testing.Platform/OutputDevice/Terminal/TestProgressState.cs @@ -7,7 +7,7 @@ namespace Microsoft.Testing.Platform.OutputDevice.Terminal; internal sealed class TestProgressState { - public TestProgressState(long id, string assembly, string? targetFramework, string? architecture, IStopwatch stopwatch) + public TestProgressState(long id, string assembly, string? targetFramework, string? architecture, IStopwatch stopwatch, bool isDiscovery) { Id = id; Assembly = assembly; @@ -15,6 +15,7 @@ public TestProgressState(long id, string assembly, string? targetFramework, stri Architecture = architecture; Stopwatch = stopwatch; AssemblyName = Path.GetFileName(assembly)!; + IsDiscovery = isDiscovery; } public string Assembly { get; } @@ -27,7 +28,9 @@ public TestProgressState(long id, string assembly, string? targetFramework, stri public IStopwatch Stopwatch { get; } - public List Messages { get; } = new(); + public int DiscoveredTests { get; internal set; } + + public List Messages { get; } = []; public int FailedTests { get; internal set; } @@ -45,7 +48,9 @@ public TestProgressState(long id, string assembly, string? targetFramework, stri public long Version { get; internal set; } - public List<(string? DisplayName, string? UID)> DiscoveredTests { get; internal set; } = new(); + public List DiscoveredTestDisplayNames { get; internal set; } = []; + + public bool IsDiscovery { get; } internal void AddError(string text) => Messages.Add(new ErrorMessage(text)); diff --git a/src/Platform/Microsoft.Testing.Platform/OutputDevice/Terminal/TestRunArtifact.cs b/src/Platform/Microsoft.Testing.Platform/OutputDevice/Terminal/TestRunArtifact.cs index 5ab5c2172d..936a213f70 100644 --- a/src/Platform/Microsoft.Testing.Platform/OutputDevice/Terminal/TestRunArtifact.cs +++ b/src/Platform/Microsoft.Testing.Platform/OutputDevice/Terminal/TestRunArtifact.cs @@ -6,4 +6,4 @@ namespace Microsoft.Testing.Platform.OutputDevice.Terminal; /// /// An artifact / attachment that was reported during run. /// -internal sealed record TestRunArtifact(bool OutOfProcess, string? Assembly, string? TargetFramework, string? Architecture, string? TestName, string Path); +internal sealed record TestRunArtifact(bool OutOfProcess, string? TestName, string Path); diff --git a/src/Platform/Microsoft.Testing.Platform/OutputDevice/TerminalOutputDevice.cs b/src/Platform/Microsoft.Testing.Platform/OutputDevice/TerminalOutputDevice.cs index ac46b81654..8701f17e0b 100644 --- a/src/Platform/Microsoft.Testing.Platform/OutputDevice/TerminalOutputDevice.cs +++ b/src/Platform/Microsoft.Testing.Platform/OutputDevice/TerminalOutputDevice.cs @@ -31,6 +31,8 @@ internal sealed partial class TerminalOutputDevice : IHotReloadPlatformOutputDev private const string TESTINGPLATFORM_CONSOLEOUTPUTDEVICE_SKIP_BANNER = nameof(TESTINGPLATFORM_CONSOLEOUTPUTDEVICE_SKIP_BANNER); #pragma warning restore SA1310 // Field names should not contain underscore + private const char Dash = '-'; + private readonly IConsole _console; private readonly ITestHostControllerInfo _testHostControllerInfo; private readonly IAsyncMonitor _asyncMonitor; @@ -42,6 +44,7 @@ internal sealed partial class TerminalOutputDevice : IHotReloadPlatformOutputDev private readonly ILoggerFactory _loggerFactory; private readonly IClock _clock; private readonly IStopPoliciesService _policiesService; + private readonly ITestApplicationCancellationTokenSource _testApplicationCancellationTokenSource; private readonly string? _longArchitecture; private readonly string? _shortArchitecture; @@ -51,7 +54,6 @@ internal sealed partial class TerminalOutputDevice : IHotReloadPlatformOutputDev // The targeted framework, .NET 8 when application specifies net8.0 private readonly string? _targetFramework; private readonly string _assemblyName; - private readonly char[] _dash = new char[] { '-' }; private TerminalTestReporter? _terminalTestReporter; private bool _firstCallTo_OnSessionStartingAsync = true; @@ -65,7 +67,7 @@ public TerminalOutputDevice( ITestApplicationModuleInfo testApplicationModuleInfo, ITestHostControllerInfo testHostControllerInfo, IAsyncMonitor asyncMonitor, IRuntimeFeature runtimeFeature, IEnvironment environment, IPlatformInformation platformInformation, ICommandLineOptions commandLineOptions, IFileLoggerInformation? fileLoggerInformation, ILoggerFactory loggerFactory, IClock clock, - IStopPoliciesService policiesService) + IStopPoliciesService policiesService, ITestApplicationCancellationTokenSource testApplicationCancellationTokenSource) { _console = console; _testHostControllerInfo = testHostControllerInfo; @@ -78,6 +80,7 @@ public TerminalOutputDevice( _loggerFactory = loggerFactory; _clock = clock; _policiesService = policiesService; + _testApplicationCancellationTokenSource = testApplicationCancellationTokenSource; if (_runtimeFeature.IsDynamicCodeSupported) { @@ -87,7 +90,7 @@ public TerminalOutputDevice( #else // RID has the operating system, we want to see that in the banner, but not next to every dll. _longArchitecture = RuntimeInformation.RuntimeIdentifier; - _shortArchitecture = GetShortArchitecture(RuntimeInformation.RuntimeIdentifier); + _shortArchitecture = TerminalOutputDevice.GetShortArchitecture(RuntimeInformation.RuntimeIdentifier); #endif _runtimeFramework = TargetFrameworkParser.GetShortTargetFramework(RuntimeInformation.FrameworkDescription); _targetFramework = TargetFrameworkParser.GetShortTargetFramework(Assembly.GetEntryAssembly()?.GetCustomAttribute()?.FrameworkDisplayName) ?? _runtimeFramework; @@ -99,6 +102,8 @@ public TerminalOutputDevice( { _bannerDisplayed = true; } + + _testApplicationCancellationTokenSource = testApplicationCancellationTokenSource; } public async Task InitializeAsync() @@ -108,7 +113,7 @@ await _policiesService.RegisterOnAbortCallbackAsync( { _terminalTestReporter?.StartCancelling(); return Task.CompletedTask; - }); + }).ConfigureAwait(false); if (_fileLoggerInformation is not null) { @@ -118,6 +123,9 @@ await _policiesService.RegisterOnAbortCallbackAsync( _isListTests = _commandLineOptions.IsOptionSet(PlatformCommandLineProvider.DiscoverTestsOptionKey); _isServerMode = _commandLineOptions.IsOptionSet(PlatformCommandLineProvider.ServerOptionKey); bool noAnsi = _commandLineOptions.IsOptionSet(TerminalTestReporterCommandLineOptionsProvider.NoAnsiOption); + + // TODO: Replace this with proper CI detection that we already have in telemetry. https://github.com/microsoft/testfx/issues/5533#issuecomment-2838893327 + bool inCI = string.Equals(_environment.GetEnvironmentVariable("TF_BUILD"), "true", StringComparison.OrdinalIgnoreCase) || string.Equals(_environment.GetEnvironmentVariable("GITHUB_ACTIONS"), "true", StringComparison.OrdinalIgnoreCase); bool noProgress = _commandLineOptions.IsOptionSet(TerminalTestReporterCommandLineOptionsProvider.NoProgressOption); // _runtimeFeature.IsHotReloadEnabled is not set to true here, even if the session will be HotReload, @@ -147,22 +155,20 @@ await _policiesService.RegisterOnAbortCallbackAsync( : !_testHostControllerInfo.IsCurrentProcessTestHostController; // This is single exe run, don't show all the details of assemblies and their summaries. - _terminalTestReporter = new TerminalTestReporter(_console, new() + _terminalTestReporter = new TerminalTestReporter(_assemblyName, _targetFramework, _shortArchitecture, _console, _testApplicationCancellationTokenSource, new() { - BaseDirectory = null, - ShowAssembly = false, - ShowAssemblyStartAndComplete = false, ShowPassedTests = showPassed, MinimumExpectedTests = PlatformCommandLineProvider.GetMinimumExpectedTests(_commandLineOptions), UseAnsi = !noAnsi, + UseCIAnsi = inCI, ShowActiveTests = true, ShowProgress = shouldShowProgress, }); } - private string GetShortArchitecture(string runtimeIdentifier) - => runtimeIdentifier.Contains('-') - ? runtimeIdentifier.Split(_dash, 2)[1] + private static string GetShortArchitecture(string runtimeIdentifier) + => runtimeIdentifier.Contains(Dash) + ? runtimeIdentifier.Split(Dash, 2)[1] : runtimeIdentifier; public Type[] DataTypesConsumed { get; } = @@ -173,16 +179,16 @@ private string GetShortArchitecture(string runtimeIdentifier) ]; /// - public string Uid { get; } = nameof(TerminalOutputDevice); + public string Uid => nameof(TerminalOutputDevice); /// - public string Version { get; } = AppVersion.DefaultSemVer; + public string Version => AppVersion.DefaultSemVer; /// - public string DisplayName { get; } = "Test Platform Console Service"; + public string DisplayName => "Test Platform Console Service"; /// - public string Description { get; } = "Test Platform default console service"; + public string Description => "Test Platform default console service"; /// public Task IsEnabledAsync() => Task.FromResult(true); @@ -191,7 +197,7 @@ private async Task LogDebugAsync(string message) { if (_logger is not null) { - await _logger.LogDebugAsync(message); + await _logger.LogDebugAsync(message).ConfigureAwait(false); } } @@ -199,7 +205,7 @@ public async Task DisplayBannerAsync(string? bannerMessage) { RoslynDebug.Assert(_terminalTestReporter is not null); - using (await _asyncMonitor.LockAsync(TimeoutHelper.DefaultHangTimeSpanTimeout)) + using (await _asyncMonitor.LockAsync(TimeoutHelper.DefaultHangTimeSpanTimeout).ConfigureAwait(false)) { if (!_bannerDisplayed) { @@ -228,7 +234,7 @@ public async Task DisplayBannerAsync(string? bannerMessage) if (_platformInformation.BuildDate is { } buildDate) { - stringBuilder.Append(CultureInfo.InvariantCulture, $" (UTC {buildDate.UtcDateTime.ToShortDateString()})"); + stringBuilder.Append(CultureInfo.InvariantCulture, $" (UTC {buildDate.UtcDateTime:d})"); } if (_runtimeFeature.IsDynamicCodeSupported) @@ -246,20 +252,20 @@ public async Task DisplayBannerAsync(string? bannerMessage) if (_fileLoggerInformation is not null) { - if (_fileLoggerInformation.SyncronousWrite) + if (_fileLoggerInformation.SynchronousWrite) { - _terminalTestReporter.WriteWarningMessage(_assemblyName, _targetFramework, _shortArchitecture, string.Format(CultureInfo.CurrentCulture, PlatformResources.DiagnosticFileLevelWithFlush, _fileLoggerInformation.LogLevel, _fileLoggerInformation.LogFile.FullName), padding: null); + _terminalTestReporter.WriteWarningMessage(string.Format(CultureInfo.CurrentCulture, PlatformResources.DiagnosticFileLevelWithFlush, _fileLoggerInformation.LogLevel, _fileLoggerInformation.LogFile.FullName), padding: null); } else { - _terminalTestReporter.WriteWarningMessage(_assemblyName, _targetFramework, _shortArchitecture, string.Format(CultureInfo.CurrentCulture, PlatformResources.DiagnosticFileLevelWithAsyncFlush, _fileLoggerInformation.LogLevel, _fileLoggerInformation.LogFile.FullName), padding: null); + _terminalTestReporter.WriteWarningMessage(string.Format(CultureInfo.CurrentCulture, PlatformResources.DiagnosticFileLevelWithAsyncFlush, _fileLoggerInformation.LogLevel, _fileLoggerInformation.LogFile.FullName), padding: null); } } } } public async Task DisplayBeforeHotReloadSessionStartAsync() - => await DisplayBeforeSessionStartAsync(); + => await DisplayBeforeSessionStartAsync().ConfigureAwait(false); public async Task DisplayBeforeSessionStartAsync() { @@ -273,15 +279,15 @@ public async Task DisplayBeforeSessionStartAsync() // Start test execution here, rather than in ShowBanner, because then we know // if we are a testHost controller or not, and if we should show progress bar. _terminalTestReporter.TestExecutionStarted(_clock.UtcNow, workerCount: 1, isDiscovery: _isListTests); - _terminalTestReporter.AssemblyRunStarted(_assemblyName, _targetFramework, _shortArchitecture); + _terminalTestReporter.AssemblyRunStarted(); if (_logger is not null && _logger.IsEnabled(LogLevel.Trace)) { - await _logger.LogTraceAsync("DisplayBeforeSessionStartAsync"); + await _logger.LogTraceAsync("DisplayBeforeSessionStartAsync").ConfigureAwait(false); } } public async Task DisplayAfterHotReloadSessionEndAsync() - => await DisplayAfterSessionEndRunInternalAsync(); + => await DisplayAfterSessionEndRunInternalAsync().ConfigureAwait(false); public async Task DisplayAfterSessionEndRunAsync() { @@ -297,18 +303,18 @@ public async Task DisplayAfterSessionEndRunAsync() return; } - await DisplayAfterSessionEndRunInternalAsync(); + await DisplayAfterSessionEndRunInternalAsync().ConfigureAwait(false); } private async Task DisplayAfterSessionEndRunInternalAsync() { RoslynDebug.Assert(_terminalTestReporter is not null); - using (await _asyncMonitor.LockAsync(TimeoutHelper.DefaultHangTimeSpanTimeout)) + using (await _asyncMonitor.LockAsync(TimeoutHelper.DefaultHangTimeSpanTimeout).ConfigureAwait(false)) { if (!_firstCallTo_OnSessionStartingAsync) { - _terminalTestReporter.AssemblyRunCompleted(_assemblyName, _targetFramework, _shortArchitecture, exitCode: null, outputData: null, errorData: null); + _terminalTestReporter.AssemblyRunCompleted(); _terminalTestReporter.TestExecutionCompleted(_clock.UtcNow); } } @@ -343,33 +349,33 @@ public async Task DisplayAsync(IOutputDeviceDataProducer producer, IOutputDevice { RoslynDebug.Assert(_terminalTestReporter is not null); - using (await _asyncMonitor.LockAsync(TimeoutHelper.DefaultHangTimeSpanTimeout)) + using (await _asyncMonitor.LockAsync(TimeoutHelper.DefaultHangTimeSpanTimeout).ConfigureAwait(false)) { switch (data) { case FormattedTextOutputDeviceData formattedTextData: - await LogDebugAsync(formattedTextData.Text); + await LogDebugAsync(formattedTextData.Text).ConfigureAwait(false); _terminalTestReporter.WriteMessage(formattedTextData.Text, formattedTextData.ForegroundColor as SystemConsoleColor, formattedTextData.Padding); break; case TextOutputDeviceData textData: - await LogDebugAsync(textData.Text); + await LogDebugAsync(textData.Text).ConfigureAwait(false); _terminalTestReporter.WriteMessage(textData.Text); break; case WarningMessageOutputDeviceData warningData: - await LogDebugAsync(warningData.Message); - _terminalTestReporter.WriteWarningMessage(_assemblyName, _targetFramework, _shortArchitecture, warningData.Message, null); + await LogDebugAsync(warningData.Message).ConfigureAwait(false); + _terminalTestReporter.WriteWarningMessage(warningData.Message, null); break; case ErrorMessageOutputDeviceData errorData: - await LogDebugAsync(errorData.Message); - _terminalTestReporter.WriteErrorMessage(_assemblyName, _targetFramework, _shortArchitecture, errorData.Message, null); + await LogDebugAsync(errorData.Message).ConfigureAwait(false); + _terminalTestReporter.WriteErrorMessage(errorData.Message, null); break; case ExceptionOutputDeviceData exceptionOutputDeviceData: - await LogDebugAsync(exceptionOutputDeviceData.Exception.ToString()); - _terminalTestReporter.WriteErrorMessage(_assemblyName, _targetFramework, _shortArchitecture, exceptionOutputDeviceData.Exception); + await LogDebugAsync(exceptionOutputDeviceData.Exception.ToString()).ConfigureAwait(false); + _terminalTestReporter.WriteErrorMessage(exceptionOutputDeviceData.Exception); break; } } @@ -397,9 +403,6 @@ public Task ConsumeAsync(IDataProducer dataProducer, IData value, CancellationTo bool isOutOfProcessArtifact = _firstCallTo_OnSessionStartingAsync; _terminalTestReporter.ArtifactAdded( isOutOfProcessArtifact, - _assemblyName, - _targetFramework, - _shortArchitecture, testNodeStateChanged.TestNode.DisplayName, artifact.FileInfo.FullName); } @@ -408,18 +411,12 @@ public Task ConsumeAsync(IDataProducer dataProducer, IData value, CancellationTo { case InProgressTestNodeStateProperty: _terminalTestReporter.TestInProgress( - _assemblyName, - _targetFramework, - _shortArchitecture, testNodeStateChanged.TestNode.Uid.Value, testNodeStateChanged.TestNode.DisplayName); break; case ErrorTestNodeStateProperty errorState: _terminalTestReporter.TestCompleted( - _assemblyName, - _targetFramework, - _shortArchitecture, testNodeStateChanged.TestNode.Uid.Value, testNodeStateChanged.TestNode.DisplayName, TestOutcome.Error, @@ -435,9 +432,6 @@ public Task ConsumeAsync(IDataProducer dataProducer, IData value, CancellationTo case FailedTestNodeStateProperty failedState: _terminalTestReporter.TestCompleted( - _assemblyName, - _targetFramework, - _shortArchitecture, testNodeStateChanged.TestNode.Uid.Value, testNodeStateChanged.TestNode.DisplayName, TestOutcome.Fail, @@ -453,9 +447,6 @@ public Task ConsumeAsync(IDataProducer dataProducer, IData value, CancellationTo case TimeoutTestNodeStateProperty timeoutState: _terminalTestReporter.TestCompleted( - _assemblyName, - _targetFramework, - _shortArchitecture, testNodeStateChanged.TestNode.Uid.Value, testNodeStateChanged.TestNode.DisplayName, TestOutcome.Timeout, @@ -471,9 +462,6 @@ public Task ConsumeAsync(IDataProducer dataProducer, IData value, CancellationTo case CancelledTestNodeStateProperty cancelledState: _terminalTestReporter.TestCompleted( - _assemblyName, - _targetFramework, - _shortArchitecture, testNodeStateChanged.TestNode.Uid.Value, testNodeStateChanged.TestNode.DisplayName, TestOutcome.Canceled, @@ -489,9 +477,6 @@ public Task ConsumeAsync(IDataProducer dataProducer, IData value, CancellationTo case PassedTestNodeStateProperty: _terminalTestReporter.TestCompleted( - _assemblyName, - _targetFramework, - _shortArchitecture, testNodeStateChanged.TestNode.Uid.Value, testNodeStateChanged.TestNode.DisplayName, outcome: TestOutcome.Passed, @@ -507,9 +492,6 @@ public Task ConsumeAsync(IDataProducer dataProducer, IData value, CancellationTo case SkippedTestNodeStateProperty skippedState: _terminalTestReporter.TestCompleted( - _assemblyName, - _targetFramework, - _shortArchitecture, testNodeStateChanged.TestNode.Uid.Value, testNodeStateChanged.TestNode.DisplayName, TestOutcome.Skipped, @@ -524,12 +506,7 @@ public Task ConsumeAsync(IDataProducer dataProducer, IData value, CancellationTo break; case DiscoveredTestNodeStateProperty: - _terminalTestReporter.TestDiscovered( - _assemblyName, - _targetFramework, - _shortArchitecture, - testNodeStateChanged.TestNode.DisplayName, - testNodeStateChanged.TestNode.Uid); + _terminalTestReporter.TestDiscovered(testNodeStateChanged.TestNode.DisplayName); break; } @@ -540,9 +517,6 @@ public Task ConsumeAsync(IDataProducer dataProducer, IData value, CancellationTo bool isOutOfProcessArtifact = _firstCallTo_OnSessionStartingAsync; _terminalTestReporter.ArtifactAdded( isOutOfProcessArtifact, - _assemblyName, - _targetFramework, - _shortArchitecture, testName: null, artifact.FileInfo.FullName); } @@ -553,9 +527,6 @@ public Task ConsumeAsync(IDataProducer dataProducer, IData value, CancellationTo bool isOutOfProcessArtifact = _firstCallTo_OnSessionStartingAsync; _terminalTestReporter.ArtifactAdded( isOutOfProcessArtifact, - _assemblyName, - _targetFramework, - _shortArchitecture, testName: null, artifact.FileInfo.FullName); } @@ -575,7 +546,7 @@ public async Task HandleProcessRoleAsync(TestProcessRole processRole) { await _policiesService.RegisterOnMaxFailedTestsCallbackAsync( async (maxFailedTests, _) => await DisplayAsync( - this, new TextOutputDeviceData(string.Format(CultureInfo.InvariantCulture, PlatformResources.ReachedMaxFailedTestsMessage, maxFailedTests)))); + this, new TextOutputDeviceData(string.Format(CultureInfo.InvariantCulture, PlatformResources.ReachedMaxFailedTestsMessage, maxFailedTests))).ConfigureAwait(false)).ConfigureAwait(false); } } } diff --git a/src/Platform/Microsoft.Testing.Platform/PublicAPI/PublicAPI.Shipped.txt b/src/Platform/Microsoft.Testing.Platform/PublicAPI/PublicAPI.Shipped.txt index 88740559f6..66e9adb2ec 100644 --- a/src/Platform/Microsoft.Testing.Platform/PublicAPI/PublicAPI.Shipped.txt +++ b/src/Platform/Microsoft.Testing.Platform/PublicAPI/PublicAPI.Shipped.txt @@ -93,7 +93,7 @@ Microsoft.Testing.Platform.Builder.ITestApplicationBuilder.BuildAsync() -> Syste Microsoft.Testing.Platform.Builder.ITestApplicationBuilder.CommandLine.get -> Microsoft.Testing.Platform.CommandLine.ICommandLineManager! Microsoft.Testing.Platform.Builder.ITestApplicationBuilder.Configuration.get -> Microsoft.Testing.Platform.Configurations.IConfigurationManager! Microsoft.Testing.Platform.Builder.ITestApplicationBuilder.Logging.get -> Microsoft.Testing.Platform.Logging.ILoggingManager! -Microsoft.Testing.Platform.Builder.ITestApplicationBuilder.RegisterTestFramework(System.Func! capabilitiesFactory, System.Func! adapterFactory) -> Microsoft.Testing.Platform.Builder.ITestApplicationBuilder! +Microsoft.Testing.Platform.Builder.ITestApplicationBuilder.RegisterTestFramework(System.Func! capabilitiesFactory, System.Func! frameworkFactory) -> Microsoft.Testing.Platform.Builder.ITestApplicationBuilder! Microsoft.Testing.Platform.Builder.ITestApplicationBuilder.TestHost.get -> Microsoft.Testing.Platform.TestHost.ITestHostManager! Microsoft.Testing.Platform.Builder.ITestApplicationBuilder.TestHostControllers.get -> Microsoft.Testing.Platform.TestHostControllers.ITestHostControllersManager! Microsoft.Testing.Platform.Builder.TestApplication @@ -174,6 +174,16 @@ Microsoft.Testing.Platform.Extensions.Messages.FailedTestNodeStateProperty.Faile Microsoft.Testing.Platform.Extensions.Messages.FileArtifact Microsoft.Testing.Platform.Extensions.Messages.FileArtifact.FileArtifact(System.IO.FileInfo! fileInfo, string! displayName, string? description = null) -> void Microsoft.Testing.Platform.Extensions.Messages.FileArtifact.FileInfo.get -> System.IO.FileInfo! +Microsoft.Testing.Platform.Extensions.Messages.FileArtifactProperty +Microsoft.Testing.Platform.Extensions.Messages.FileArtifactProperty.Deconstruct(out System.IO.FileInfo! FileInfo, out string! DisplayName, out string? Description) -> void +Microsoft.Testing.Platform.Extensions.Messages.FileArtifactProperty.Description.get -> string? +Microsoft.Testing.Platform.Extensions.Messages.FileArtifactProperty.Description.init -> void +Microsoft.Testing.Platform.Extensions.Messages.FileArtifactProperty.DisplayName.get -> string! +Microsoft.Testing.Platform.Extensions.Messages.FileArtifactProperty.DisplayName.init -> void +Microsoft.Testing.Platform.Extensions.Messages.FileArtifactProperty.FileArtifactProperty(Microsoft.Testing.Platform.Extensions.Messages.FileArtifactProperty! original) -> void +Microsoft.Testing.Platform.Extensions.Messages.FileArtifactProperty.FileArtifactProperty(System.IO.FileInfo! FileInfo, string! DisplayName, string? Description = null) -> void +Microsoft.Testing.Platform.Extensions.Messages.FileArtifactProperty.FileInfo.get -> System.IO.FileInfo! +Microsoft.Testing.Platform.Extensions.Messages.FileArtifactProperty.FileInfo.init -> void Microsoft.Testing.Platform.Extensions.Messages.FileLocationProperty Microsoft.Testing.Platform.Extensions.Messages.FileLocationProperty.FileLocationProperty(string! FilePath, Microsoft.Testing.Platform.Extensions.Messages.LinePositionSpan LineSpan) -> void Microsoft.Testing.Platform.Extensions.Messages.FileLocationProperty.FilePath.get -> string! @@ -252,6 +262,9 @@ Microsoft.Testing.Platform.Extensions.Messages.TestMetadataProperty.Value.init - Microsoft.Testing.Platform.Extensions.Messages.TestMethodIdentifierProperty Microsoft.Testing.Platform.Extensions.Messages.TestMethodIdentifierProperty.AssemblyFullName.get -> string! Microsoft.Testing.Platform.Extensions.Messages.TestMethodIdentifierProperty.AssemblyFullName.init -> void +Microsoft.Testing.Platform.Extensions.Messages.TestMethodIdentifierProperty.Deconstruct(out string! AssemblyFullName, out string! Namespace, out string! TypeName, out string! MethodName, out int MethodArity, out string![]! ParameterTypeFullNames, out string! ReturnTypeFullName) -> void +Microsoft.Testing.Platform.Extensions.Messages.TestMethodIdentifierProperty.MethodArity.get -> int +Microsoft.Testing.Platform.Extensions.Messages.TestMethodIdentifierProperty.MethodArity.init -> void Microsoft.Testing.Platform.Extensions.Messages.TestMethodIdentifierProperty.MethodName.get -> string! Microsoft.Testing.Platform.Extensions.Messages.TestMethodIdentifierProperty.MethodName.init -> void Microsoft.Testing.Platform.Extensions.Messages.TestMethodIdentifierProperty.Namespace.get -> string! @@ -260,6 +273,7 @@ Microsoft.Testing.Platform.Extensions.Messages.TestMethodIdentifierProperty.Para Microsoft.Testing.Platform.Extensions.Messages.TestMethodIdentifierProperty.ParameterTypeFullNames.init -> void Microsoft.Testing.Platform.Extensions.Messages.TestMethodIdentifierProperty.ReturnTypeFullName.get -> string! Microsoft.Testing.Platform.Extensions.Messages.TestMethodIdentifierProperty.ReturnTypeFullName.init -> void +Microsoft.Testing.Platform.Extensions.Messages.TestMethodIdentifierProperty.TestMethodIdentifierProperty(string! AssemblyFullName, string! Namespace, string! TypeName, string! MethodName, int MethodArity, string![]! ParameterTypeFullNames, string! ReturnTypeFullName) -> void Microsoft.Testing.Platform.Extensions.Messages.TestMethodIdentifierProperty.TestMethodIdentifierProperty(string! AssemblyFullName, string! Namespace, string! TypeName, string! MethodName, string![]! ParameterTypeFullNames, string! ReturnTypeFullName) -> void Microsoft.Testing.Platform.Extensions.Messages.TestMethodIdentifierProperty.TypeName.get -> string! Microsoft.Testing.Platform.Extensions.Messages.TestMethodIdentifierProperty.TypeName.init -> void @@ -344,6 +358,7 @@ Microsoft.Testing.Platform.Extensions.TestHost.IDataConsumer.DataTypesConsumed.g Microsoft.Testing.Platform.Extensions.TestHost.ITestApplicationLifecycleCallbacks Microsoft.Testing.Platform.Extensions.TestHost.ITestApplicationLifecycleCallbacks.AfterRunAsync(int exitCode, System.Threading.CancellationToken cancellation) -> System.Threading.Tasks.Task! Microsoft.Testing.Platform.Extensions.TestHost.ITestApplicationLifecycleCallbacks.BeforeRunAsync(System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task! +Microsoft.Testing.Platform.Extensions.TestHost.ITestHostApplicationLifetime Microsoft.Testing.Platform.Extensions.TestHost.ITestHostExtension Microsoft.Testing.Platform.Extensions.TestHost.ITestSessionLifetimeHandler Microsoft.Testing.Platform.Extensions.TestHost.ITestSessionLifetimeHandler.OnTestSessionFinishingAsync(Microsoft.Testing.Platform.TestHost.SessionUid sessionUid, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task! @@ -449,6 +464,7 @@ Microsoft.Testing.Platform.TestHost.ITestHostManager Microsoft.Testing.Platform.TestHost.ITestHostManager.AddDataConsumer(System.Func! dataConsumerFactory) -> void Microsoft.Testing.Platform.TestHost.ITestHostManager.AddDataConsumer(Microsoft.Testing.Platform.Extensions.CompositeExtensionFactory! compositeServiceFactory) -> void Microsoft.Testing.Platform.TestHost.ITestHostManager.AddTestApplicationLifecycleCallbacks(System.Func! testApplicationLifecycleCallbacks) -> void +Microsoft.Testing.Platform.TestHost.ITestHostManager.AddTestHostApplicationLifetime(System.Func! testHostApplicationLifetimeFactory) -> void Microsoft.Testing.Platform.TestHost.ITestHostManager.AddTestSessionLifetimeHandle(System.Func! testSessionLifetimeHandleFactory) -> void Microsoft.Testing.Platform.TestHost.ITestHostManager.AddTestSessionLifetimeHandle(Microsoft.Testing.Platform.Extensions.CompositeExtensionFactory! compositeServiceFactory) -> void Microsoft.Testing.Platform.TestHost.SessionUid @@ -470,6 +486,9 @@ override Microsoft.Testing.Platform.Extensions.CommandLine.CommandLineOption.Equ override Microsoft.Testing.Platform.Extensions.CommandLine.CommandLineOption.GetHashCode() -> int override Microsoft.Testing.Platform.Extensions.Messages.DataWithSessionUid.ToString() -> string! override Microsoft.Testing.Platform.Extensions.Messages.FileArtifact.ToString() -> string! +override Microsoft.Testing.Platform.Extensions.Messages.FileArtifactProperty.Equals(object? obj) -> bool +override Microsoft.Testing.Platform.Extensions.Messages.FileArtifactProperty.GetHashCode() -> int +override Microsoft.Testing.Platform.Extensions.Messages.FileArtifactProperty.ToString() -> string! override Microsoft.Testing.Platform.Extensions.Messages.PropertyBagData.ToString() -> string! override Microsoft.Testing.Platform.Extensions.Messages.SessionFileArtifact.ToString() -> string! override Microsoft.Testing.Platform.Extensions.Messages.TestNode.ToString() -> string! @@ -487,6 +506,8 @@ static Microsoft.Testing.Platform.Configurations.ConfigurationExtensions.GetTest static Microsoft.Testing.Platform.Extensions.CommandLine.ArgumentArity.operator !=(Microsoft.Testing.Platform.Extensions.CommandLine.ArgumentArity left, Microsoft.Testing.Platform.Extensions.CommandLine.ArgumentArity right) -> bool static Microsoft.Testing.Platform.Extensions.CommandLine.ArgumentArity.operator ==(Microsoft.Testing.Platform.Extensions.CommandLine.ArgumentArity left, Microsoft.Testing.Platform.Extensions.CommandLine.ArgumentArity right) -> bool static Microsoft.Testing.Platform.Extensions.Messages.DiscoveredTestNodeStateProperty.CachedInstance.get -> Microsoft.Testing.Platform.Extensions.Messages.DiscoveredTestNodeStateProperty! +static Microsoft.Testing.Platform.Extensions.Messages.FileArtifactProperty.operator !=(Microsoft.Testing.Platform.Extensions.Messages.FileArtifactProperty? left, Microsoft.Testing.Platform.Extensions.Messages.FileArtifactProperty? right) -> bool +static Microsoft.Testing.Platform.Extensions.Messages.FileArtifactProperty.operator ==(Microsoft.Testing.Platform.Extensions.Messages.FileArtifactProperty? left, Microsoft.Testing.Platform.Extensions.Messages.FileArtifactProperty? right) -> bool static Microsoft.Testing.Platform.Extensions.Messages.InProgressTestNodeStateProperty.CachedInstance.get -> Microsoft.Testing.Platform.Extensions.Messages.InProgressTestNodeStateProperty! static Microsoft.Testing.Platform.Extensions.Messages.PassedTestNodeStateProperty.CachedInstance.get -> Microsoft.Testing.Platform.Extensions.Messages.PassedTestNodeStateProperty! static Microsoft.Testing.Platform.Extensions.Messages.SkippedTestNodeStateProperty.CachedInstance.get -> Microsoft.Testing.Platform.Extensions.Messages.SkippedTestNodeStateProperty! @@ -529,3 +550,7 @@ static readonly Microsoft.Testing.Platform.Extensions.CommandLine.ArgumentArity. static readonly Microsoft.Testing.Platform.Extensions.CommandLine.ArgumentArity.ZeroOrOne -> Microsoft.Testing.Platform.Extensions.CommandLine.ArgumentArity static readonly Microsoft.Testing.Platform.TestHost.WellKnownClients.TestingPlatformConsole -> string! static readonly Microsoft.Testing.Platform.TestHost.WellKnownClients.VisualStudio -> string! +virtual Microsoft.Testing.Platform.Extensions.Messages.FileArtifactProperty.$() -> Microsoft.Testing.Platform.Extensions.Messages.FileArtifactProperty! +virtual Microsoft.Testing.Platform.Extensions.Messages.FileArtifactProperty.EqualityContract.get -> System.Type! +virtual Microsoft.Testing.Platform.Extensions.Messages.FileArtifactProperty.Equals(Microsoft.Testing.Platform.Extensions.Messages.FileArtifactProperty? other) -> bool +virtual Microsoft.Testing.Platform.Extensions.Messages.FileArtifactProperty.PrintMembers(System.Text.StringBuilder! builder) -> bool diff --git a/src/Platform/Microsoft.Testing.Platform/PublicAPI/PublicAPI.Unshipped.txt b/src/Platform/Microsoft.Testing.Platform/PublicAPI/PublicAPI.Unshipped.txt index 27ec037846..7dc5c58110 100644 --- a/src/Platform/Microsoft.Testing.Platform/PublicAPI/PublicAPI.Unshipped.txt +++ b/src/Platform/Microsoft.Testing.Platform/PublicAPI/PublicAPI.Unshipped.txt @@ -1,24 +1 @@ #nullable enable -Microsoft.Testing.Platform.Extensions.Messages.FileArtifactProperty -Microsoft.Testing.Platform.Extensions.Messages.FileArtifactProperty.Deconstruct(out System.IO.FileInfo! FileInfo, out string! DisplayName, out string? Description) -> void -Microsoft.Testing.Platform.Extensions.Messages.FileArtifactProperty.Description.get -> string? -Microsoft.Testing.Platform.Extensions.Messages.FileArtifactProperty.Description.init -> void -Microsoft.Testing.Platform.Extensions.Messages.FileArtifactProperty.DisplayName.get -> string! -Microsoft.Testing.Platform.Extensions.Messages.FileArtifactProperty.DisplayName.init -> void -Microsoft.Testing.Platform.Extensions.Messages.FileArtifactProperty.FileArtifactProperty(Microsoft.Testing.Platform.Extensions.Messages.FileArtifactProperty! original) -> void -Microsoft.Testing.Platform.Extensions.Messages.FileArtifactProperty.FileArtifactProperty(System.IO.FileInfo! FileInfo, string! DisplayName, string? Description = null) -> void -Microsoft.Testing.Platform.Extensions.Messages.FileArtifactProperty.FileInfo.get -> System.IO.FileInfo! -Microsoft.Testing.Platform.Extensions.Messages.FileArtifactProperty.FileInfo.init -> void -Microsoft.Testing.Platform.Extensions.Messages.TestMethodIdentifierProperty.Deconstruct(out string! AssemblyFullName, out string! Namespace, out string! TypeName, out string! MethodName, out int MethodArity, out string![]! ParameterTypeFullNames, out string! ReturnTypeFullName) -> void -Microsoft.Testing.Platform.Extensions.Messages.TestMethodIdentifierProperty.MethodArity.get -> int -Microsoft.Testing.Platform.Extensions.Messages.TestMethodIdentifierProperty.MethodArity.init -> void -Microsoft.Testing.Platform.Extensions.Messages.TestMethodIdentifierProperty.TestMethodIdentifierProperty(string! AssemblyFullName, string! Namespace, string! TypeName, string! MethodName, int MethodArity, string![]! ParameterTypeFullNames, string! ReturnTypeFullName) -> void -override Microsoft.Testing.Platform.Extensions.Messages.FileArtifactProperty.Equals(object? obj) -> bool -override Microsoft.Testing.Platform.Extensions.Messages.FileArtifactProperty.GetHashCode() -> int -override Microsoft.Testing.Platform.Extensions.Messages.FileArtifactProperty.ToString() -> string! -static Microsoft.Testing.Platform.Extensions.Messages.FileArtifactProperty.operator !=(Microsoft.Testing.Platform.Extensions.Messages.FileArtifactProperty? left, Microsoft.Testing.Platform.Extensions.Messages.FileArtifactProperty? right) -> bool -static Microsoft.Testing.Platform.Extensions.Messages.FileArtifactProperty.operator ==(Microsoft.Testing.Platform.Extensions.Messages.FileArtifactProperty? left, Microsoft.Testing.Platform.Extensions.Messages.FileArtifactProperty? right) -> bool -virtual Microsoft.Testing.Platform.Extensions.Messages.FileArtifactProperty.$() -> Microsoft.Testing.Platform.Extensions.Messages.FileArtifactProperty! -virtual Microsoft.Testing.Platform.Extensions.Messages.FileArtifactProperty.EqualityContract.get -> System.Type! -virtual Microsoft.Testing.Platform.Extensions.Messages.FileArtifactProperty.Equals(Microsoft.Testing.Platform.Extensions.Messages.FileArtifactProperty? other) -> bool -virtual Microsoft.Testing.Platform.Extensions.Messages.FileArtifactProperty.PrintMembers(System.Text.StringBuilder! builder) -> bool diff --git a/src/Platform/Microsoft.Testing.Platform/Requests/ConsoleTestExecutionFilterFactory.cs b/src/Platform/Microsoft.Testing.Platform/Requests/ConsoleTestExecutionFilterFactory.cs index 14f7305ec4..5e7ef78150 100644 --- a/src/Platform/Microsoft.Testing.Platform/Requests/ConsoleTestExecutionFilterFactory.cs +++ b/src/Platform/Microsoft.Testing.Platform/Requests/ConsoleTestExecutionFilterFactory.cs @@ -2,6 +2,7 @@ // 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.Messages; using Microsoft.Testing.Platform.Helpers; using Microsoft.Testing.Platform.Resources; @@ -21,8 +22,18 @@ internal sealed class ConsoleTestExecutionFilterFactory(ICommandLineOptions comm public Task IsEnabledAsync() => Task.FromResult(true); - public Task<(bool Success, ITestExecutionFilter? TestExecutionFilter)> TryCreateAsync() => - _commandLineService.TryGetOptionArgumentList(TreeNodeFilterCommandLineOptionsProvider.TreenodeFilter, out string[]? filter) - ? Task.FromResult((true, (ITestExecutionFilter?)new TreeNodeFilter(filter[0]))) - : Task.FromResult((true, (ITestExecutionFilter?)new NopFilter())); + public Task<(bool Success, ITestExecutionFilter? TestExecutionFilter)> TryCreateAsync() + { + bool hasTreenodeFilter = _commandLineService.TryGetOptionArgumentList(TreeNodeFilterCommandLineOptionsProvider.TreenodeFilter, out string[]? treenodeFilter); + bool hasTestNodeUidFilter = _commandLineService.TryGetOptionArgumentList(PlatformCommandLineProvider.FilterUidOptionKey, out string[]? uidFilter); + ITestExecutionFilter filter = (hasTreenodeFilter, hasTestNodeUidFilter) switch + { + (true, true) => throw new NotSupportedException(PlatformResources.OnlyOneFilterSupported), + (true, false) => new TreeNodeFilter(treenodeFilter![0]), + (false, true) => new TestNodeUidListFilter([.. uidFilter!.Select(x => new TestNodeUid(x))]), + (false, false) => new NopFilter(), + }; + + return Task.FromResult<(bool, ITestExecutionFilter?)>((true, filter)); + } } diff --git a/src/Platform/Microsoft.Testing.Platform/Requests/ConsoleTestExecutionRequestFactory.cs b/src/Platform/Microsoft.Testing.Platform/Requests/ConsoleTestExecutionRequestFactory.cs index e4da6701f6..b31feb6d14 100644 --- a/src/Platform/Microsoft.Testing.Platform/Requests/ConsoleTestExecutionRequestFactory.cs +++ b/src/Platform/Microsoft.Testing.Platform/Requests/ConsoleTestExecutionRequestFactory.cs @@ -15,7 +15,7 @@ internal sealed class ConsoleTestExecutionRequestFactory(ICommandLineOptions com public async Task CreateRequestAsync(TestSessionContext session) { - (bool created, ITestExecutionFilter? testExecutionFilter) = await _testExecutionFilterFactory.TryCreateAsync(); + (bool created, ITestExecutionFilter? testExecutionFilter) = await _testExecutionFilterFactory.TryCreateAsync().ConfigureAwait(false); if (!created) { throw new InvalidOperationException(PlatformResources.CannotCreateTestExecutionFilterErrorMessage); diff --git a/src/Platform/Microsoft.Testing.Platform/Requests/ITestExecutionFilterFactory.cs b/src/Platform/Microsoft.Testing.Platform/Requests/ITestExecutionFilterFactory.cs index cc80518805..37b51a3d82 100644 --- a/src/Platform/Microsoft.Testing.Platform/Requests/ITestExecutionFilterFactory.cs +++ b/src/Platform/Microsoft.Testing.Platform/Requests/ITestExecutionFilterFactory.cs @@ -7,5 +7,5 @@ namespace Microsoft.Testing.Platform.Requests; internal interface ITestExecutionFilterFactory : IExtension { - public Task<(bool Success, ITestExecutionFilter? TestExecutionFilter)> TryCreateAsync(); + Task<(bool Success, ITestExecutionFilter? TestExecutionFilter)> TryCreateAsync(); } diff --git a/src/Platform/Microsoft.Testing.Platform/Requests/TestHostTestFrameworkInvoker.cs b/src/Platform/Microsoft.Testing.Platform/Requests/TestHostTestFrameworkInvoker.cs index f44eb1e82b..0ba1974901 100644 --- a/src/Platform/Microsoft.Testing.Platform/Requests/TestHostTestFrameworkInvoker.cs +++ b/src/Platform/Microsoft.Testing.Platform/Requests/TestHostTestFrameworkInvoker.cs @@ -37,40 +37,40 @@ public async Task ExecuteAsync(ITestFramework testFramework, ClientInfo client, { ILogger logger = ServiceProvider.GetLoggerFactory().CreateLogger(); - await logger.LogInformationAsync($"Test framework UID: '{testFramework.Uid}' Version: '{testFramework.Version}' DisplayName: '{testFramework.DisplayName}' Description: '{testFramework.Description}'"); + await logger.LogInformationAsync($"Test framework UID: '{testFramework.Uid}' Version: '{testFramework.Version}' DisplayName: '{testFramework.DisplayName}' Description: '{testFramework.Description}'").ConfigureAwait(false); foreach (ICapability capability in ServiceProvider.GetTestFrameworkCapabilities().Capabilities) { if (capability is ITestNodesTreeFilterTestFrameworkCapability testNodesTreeFilterCapability) { - await logger.LogInformationAsync($"ITestNodesTreeFilterCapability.IsSupported: {testNodesTreeFilterCapability.IsSupported}"); + await logger.LogInformationAsync($"ITestNodesTreeFilterCapability.IsSupported: {testNodesTreeFilterCapability.IsSupported}").ConfigureAwait(false); } } DateTimeOffset startTime = DateTimeOffset.UtcNow; var stopwatch = Stopwatch.StartNew(); SessionUid sessionId = ServiceProvider.GetTestSessionContext().SessionId; - CreateTestSessionResult createTestSessionResult = await testFramework.CreateTestSessionAsync(new(sessionId, client, cancellationToken)); - await HandleTestSessionResultAsync(createTestSessionResult.IsSuccess, createTestSessionResult.WarningMessage, createTestSessionResult.ErrorMessage); + CreateTestSessionResult createTestSessionResult = await testFramework.CreateTestSessionAsync(new(sessionId, client, cancellationToken)).ConfigureAwait(false); + await HandleTestSessionResultAsync(createTestSessionResult.IsSuccess, createTestSessionResult.WarningMessage, createTestSessionResult.ErrorMessage).ConfigureAwait(false); ITestExecutionRequestFactory testExecutionRequestFactory = ServiceProvider.GetTestExecutionRequestFactory(); - TestExecutionRequest request = await testExecutionRequestFactory.CreateRequestAsync(new(sessionId, client)); + TestExecutionRequest request = await testExecutionRequestFactory.CreateRequestAsync(new(sessionId, client)).ConfigureAwait(false); IMessageBus messageBus = ServiceProvider.GetMessageBus(); // Execute the test request - await ExecuteRequestAsync(testFramework, request, messageBus, cancellationToken); + await ExecuteRequestAsync(testFramework, request, messageBus, cancellationToken).ConfigureAwait(false); - CloseTestSessionResult closeTestSessionResult = await testFramework.CloseTestSessionAsync(new(sessionId, client, cancellationToken)); - await HandleTestSessionResultAsync(closeTestSessionResult.IsSuccess, closeTestSessionResult.WarningMessage, closeTestSessionResult.ErrorMessage); + CloseTestSessionResult closeTestSessionResult = await testFramework.CloseTestSessionAsync(new(sessionId, client, cancellationToken)).ConfigureAwait(false); + await HandleTestSessionResultAsync(closeTestSessionResult.IsSuccess, closeTestSessionResult.WarningMessage, closeTestSessionResult.ErrorMessage).ConfigureAwait(false); DateTimeOffset endTime = DateTimeOffset.UtcNow; - await messageBus.PublishAsync(this, new TestRequestExecutionTimeInfo(new TimingInfo(startTime, endTime, stopwatch.Elapsed))); + await messageBus.PublishAsync(this, new TestRequestExecutionTimeInfo(new TimingInfo(startTime, endTime, stopwatch.Elapsed))).ConfigureAwait(false); } public virtual async Task ExecuteRequestAsync(ITestFramework testFramework, TestExecutionRequest request, IMessageBus messageBus, CancellationToken cancellationToken) { using SemaphoreSlim requestSemaphore = new(0, 1); - await testFramework.ExecuteRequestAsync(new(request, messageBus, new SemaphoreSlimRequestCompleteNotifier(requestSemaphore), cancellationToken)); - await requestSemaphore.WaitAsync(cancellationToken); + await testFramework.ExecuteRequestAsync(new(request, messageBus, new SemaphoreSlimRequestCompleteNotifier(requestSemaphore), cancellationToken)).ConfigureAwait(false); + await requestSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false); } private async Task HandleTestSessionResultAsync(bool isSuccess, string? warningMessage, string? errorMessage) @@ -78,14 +78,14 @@ private async Task HandleTestSessionResultAsync(bool isSuccess, string? warningM if (warningMessage is not null) { IOutputDevice outputDisplay = ServiceProvider.GetOutputDevice(); - await outputDisplay.DisplayAsync(this, new WarningMessageOutputDeviceData(warningMessage)); + await outputDisplay.DisplayAsync(this, new WarningMessageOutputDeviceData(warningMessage)).ConfigureAwait(false); } if (!isSuccess) { ITestApplicationProcessExitCode testApplicationProcessExitCode = ServiceProvider.GetTestApplicationProcessExitCode(); await testApplicationProcessExitCode.SetTestAdapterTestSessionFailureAsync(errorMessage - ?? PlatformResources.TestHostAdapterInvokerFailedTestSessionErrorMessage); + ?? PlatformResources.TestHostAdapterInvokerFailedTestSessionErrorMessage).ConfigureAwait(false); } } } diff --git a/src/Platform/Microsoft.Testing.Platform/Requests/TreeNodeFilter/OperatorKind.cs b/src/Platform/Microsoft.Testing.Platform/Requests/TreeNodeFilter/OperatorKind.cs index b9c7da2819..68de3b9293 100644 --- a/src/Platform/Microsoft.Testing.Platform/Requests/TreeNodeFilter/OperatorKind.cs +++ b/src/Platform/Microsoft.Testing.Platform/Requests/TreeNodeFilter/OperatorKind.cs @@ -55,4 +55,9 @@ internal enum OperatorKind /// Operator used for combining multiple filters with a logical AND. /// And, + + /// + /// Operator used to negate an expression. + /// + UnaryNot, } diff --git a/src/Platform/Microsoft.Testing.Platform/Requests/TreeNodeFilter/TreeNodeFilter.cs b/src/Platform/Microsoft.Testing.Platform/Requests/TreeNodeFilter/TreeNodeFilter.cs index 7cb4ff2a9e..e7d3eaff93 100644 --- a/src/Platform/Microsoft.Testing.Platform/Requests/TreeNodeFilter/TreeNodeFilter.cs +++ b/src/Platform/Microsoft.Testing.Platform/Requests/TreeNodeFilter/TreeNodeFilter.cs @@ -77,7 +77,7 @@ private static List ParseFilter(string filter) // of an expression operators are not allowed. bool isOperatorAllowed = false; bool isPropAllowed = false; - + bool lastWasOpenParen = false; OperatorKind topStackOperator; foreach (string token in TokenizeFilter(filter)) @@ -225,6 +225,10 @@ private static List ParseFilter(string filter) isPropAllowed = false; break; + case "!" when lastWasOpenParen: + operatorStack.Push(OperatorKind.UnaryNot); + break; + default: expressionStack.Push(new ValueExpression(token)); @@ -232,6 +236,8 @@ private static List ParseFilter(string filter) isPropAllowed = true; break; } + + lastWasOpenParen = token == "("; } // Note: What we should end with (as long as the expression is a valid filter) @@ -262,11 +268,9 @@ static void ProcessHigherPrecedenceOperators( Stack operatorStack, OperatorKind currentOp) { - OperatorKind topStackOperator; - while (operatorStack.Count != 0 && operatorStack.Peek() > currentOp) { - topStackOperator = operatorStack.Pop(); + OperatorKind topStackOperator = operatorStack.Pop(); ProcessStackOperator(topStackOperator, expressionStack, operatorStack); break; } @@ -352,6 +356,11 @@ private static void ProcessStackOperator(OperatorKind op, Stack TokenizeFilter(string filter) yield return "!="; i++; } + else if (i - 1 >= 0 && filter[i - 1] == '(') + { + // Note: If we have a ! at the start of an expression, we should + // treat it as a NOT operator. + yield return "!"; + } else { goto default; @@ -550,7 +565,7 @@ private static bool MatchProperties( => propertyExpr switch { PropertyExpression { PropertyName: var propExpr, Value: var valueExpr } - => properties.AsEnumerable().Any(prop => prop is KeyValuePairStringProperty kvpProperty && propExpr.Regex.IsMatch(kvpProperty.Key) && valueExpr.Regex.IsMatch(kvpProperty.Value)), + => properties.AsEnumerable().Any(prop => IsMatchingProperty(prop, propExpr, valueExpr)), OperatorExpression { Op: FilterOperator.Or, SubExpressions: var subExprs } => subExprs.Any(expr => MatchProperties(expr, properties)), OperatorExpression { Op: FilterOperator.And, SubExpressions: var subExprs } @@ -559,4 +574,14 @@ private static bool MatchProperties( => !MatchProperties(subExprs.Single(), properties), _ => throw ApplicationStateGuard.Unreachable(), }; + + private static bool IsMatchingProperty(IProperty prop, ValueExpression propExpr, ValueExpression valueExpr) => + prop switch + { +#pragma warning disable CS0618 // Type or member is obsolete + KeyValuePairStringProperty kvpProperty => propExpr.Regex.IsMatch(kvpProperty.Key) && valueExpr.Regex.IsMatch(kvpProperty.Value), +#pragma warning restore CS0618 // Type or member is obsolete + TestMetadataProperty testMetadataProperty => propExpr.Regex.IsMatch(testMetadataProperty.Key) && valueExpr.Regex.IsMatch(testMetadataProperty.Value), + _ => false, + }; } diff --git a/src/Platform/Microsoft.Testing.Platform/Resources/PlatformResources.resx b/src/Platform/Microsoft.Testing.Platform/Resources/PlatformResources.resx index b894e8fe79..5b7a17db4f 100644 --- a/src/Platform/Microsoft.Testing.Platform/Resources/PlatformResources.resx +++ b/src/Platform/Microsoft.Testing.Platform/Resources/PlatformResources.resx @@ -136,7 +136,7 @@ Extension of type '{0}' is not implementing the required '{1}' interface - Another extension with same the same UID '{0}' has already been registered. Registered extension is of type '{1}' + Extensions with the same UID '{0}' have already been registered. Registered extensions are of types: {1} A duplicate key '{0}' was found @@ -444,12 +444,6 @@ The default is TestResults in the directory that contains the test application.< JsonRpc server to client handshake, implementation based on the test platform protocol specification. - - Starting server. Listening on port '{0}' - - - The communication protocol '{0}' is not supported - Telemetry --------- @@ -556,6 +550,9 @@ Read more about Microsoft Testing Platform telemetry: https://aka.ms/testingplat Total + + total + Zero tests ran @@ -563,6 +560,9 @@ Read more about Microsoft Testing Platform telemetry: https://aka.ms/testingplat Console is already in batching mode. Exception that is thrown when console is already collecting input into a batch (into a string builder), and code asks to enable batching mode again. + + duration + failed with {0} error(s) @@ -713,4 +713,16 @@ Takes one argument as string in the format <value>[h|m|s] where 'value' is The current test framework does not implement 'IGracefulStopTestExecutionCapability' which is required for '--maximum-failed-tests' feature. - + + Provides a list of test node UIDs to filter by. + + + Passing both '--treenode-filter' and '--filter-uid' is unsupported. + + + Allows to pause execution in order to attach to the process for debug purposes. + + + succeeded + + \ No newline at end of file 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 850364800b..2204ce6b52 100644 --- a/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.cs.xlf +++ b/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.cs.xlf @@ -224,6 +224,11 @@ Zjišťování testů z + + duration + duration + + Provider '{0}' (UID: {1}) failed with error: {2} Zprostředkovatel {0} (UID: {1}) selhal s chybou: {2}. @@ -250,8 +255,8 @@ - Another extension with same the same UID '{0}' has already been registered. Registered extension is of type '{1}' - Už je zaregistrované jiné rozšíření se stejným UID {0}. Zaregistrované rozšíření je typu {1}. + Extensions with the same UID '{0}' have already been registered. Registered extensions are of types: {1} + Rozšíření se stejným UID {0} již byla zaregistrována. Registrovaná rozšíření jsou těchto typů: {1} @@ -431,6 +436,11 @@ Nenalezeno + + Passing both '--treenode-filter' and '--filter-uid' is unsupported. + Předávání možností --treenode-filter a --filter-uid není podporováno. + + Out of process file artifacts produced: Vytvořené artefakty souboru mimo proces: @@ -461,6 +471,11 @@ Určuje soubor testconfig.json. + + Allows to pause execution in order to attach to the process for debug purposes. + Umožňuje pozastavit provádění, aby se mohlo připojit k procesu pro účely ladění. + + 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). @@ -533,6 +548,11 @@ Dostupné hodnoty jsou Trace, Debug, Information, Warning, Error a Critical.„ --{0}“ očekává jeden argument int PID. + + Provides a list of test node UIDs to filter by. + Poskytuje seznam identifikátorů UID testovacích uzlů, podle kterých lze filtrovat. + + Show the command line help. Umožňuje zobrazit nápovědu příkazového řádku. @@ -733,11 +753,6 @@ Může mít jenom jeden argument jako řetězec ve formátu <value>[h|m|s] Standardní výstup - - Starting server. Listening on port '{0}' - Spouští se server. Naslouchání na portu {0} - - Starting test session. Spouští se testovací relace. @@ -748,6 +763,11 @@ Může mít jenom jeden argument jako řetězec ve formátu <value>[h|m|s] Spouští se testovací relace. Cesta k souboru protokolu je '{0}'. + + succeeded + succeeded + + An 'ITestExecutionFilterFactory' factory is already set Objekt pro vytváření ITestExecutionFilterFactory je už nastavený. @@ -952,11 +972,6 @@ Platné hodnoty jsou Normal a Detailed. Výchozí hodnota je Normal. Neočekávaný stav v souboru {0} na řádku {1} - - The communication protocol '{0}' is not supported - Komunikační protokol {0} není podporován. - - [ServerTestHost.OnTaskSchedulerUnobservedTaskException] Unhandled exception: {0} [ServerTestHost.OnTaskSchedulerUnobservedTaskException] Neošetřená výjimka: {0} @@ -972,6 +987,11 @@ Platné hodnoty jsou Normal a Detailed. Výchozí hodnota je Normal. Spustila se nula testů + + total + total + + \ 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 f945fff2db..3678a00c80 100644 --- a/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.de.xlf +++ b/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.de.xlf @@ -224,6 +224,11 @@ Tests ermitteln aus + + duration + duration + + Provider '{0}' (UID: {1}) failed with error: {2} Anbieter "{0}" (UID: {1}) ist mit folgendem Fehler fehlgeschlagen: {2} @@ -250,8 +255,8 @@ - Another extension with same the same UID '{0}' has already been registered. Registered extension is of type '{1}' - Eine andere Erweiterung mit derselben UID "{0}" wurde bereits registriert. Die registrierte Erweiterung ist vom Typ "{1}" + Extensions with the same UID '{0}' have already been registered. Registered extensions are of types: {1} + Erweiterungen mit derselben UID „{0}“ wurden bereits registriert. Registrierte Erweiterungen haben die Typen: {1} @@ -431,6 +436,11 @@ Nicht gefunden + + Passing both '--treenode-filter' and '--filter-uid' is unsupported. + Das gleichzeitige Übergeben von „--treenode-filter“ und „--filter-uid“ wird nicht unterstützt. + + Out of process file artifacts produced: Nicht verarbeitete Dateiartefakte erstellt: @@ -461,6 +471,11 @@ Gibt eine testconfig.json-Datei an. + + Allows to pause execution in order to attach to the process for debug purposes. + Ermöglicht das Anhalten der Ausführung zum Anfügen an den Prozess zum Debuggen. + + 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). @@ -533,6 +548,11 @@ Die verfügbaren Werte sind "Trace", "Debug", "Information", "Warning", "Error" "--{0}" erwartet ein einzelnes int-PID-Argument. + + Provides a list of test node UIDs to filter by. + Stellt eine Liste der UIDs von Testknoten bereit, nach denen gefiltert werden kann. + + Show the command line help. Zeigen Sie die Hilfe zur Befehlszeile an. @@ -733,11 +753,6 @@ Nimmt ein Argument als Zeichenfolge im Format <value>[h|m|s], wobei "value Standardausgabe - - Starting server. Listening on port '{0}' - Server wird gestartet. An Port "{0}" lauschen - - Starting test session. Die Testsitzung wird gestartet. @@ -748,6 +763,11 @@ Nimmt ein Argument als Zeichenfolge im Format <value>[h|m|s], wobei "value Die Testsitzung wird gestartet. Der Protokolldateipfad ist '{0}'. + + succeeded + succeeded + + An 'ITestExecutionFilterFactory' factory is already set Eine "ITestExecutionFilterFactory"-Factory ist bereits festgelegt @@ -952,11 +972,6 @@ Gültige Werte sind „Normal“, „Detailed“. Der Standardwert ist „Normal Unerwarteter Status in Datei "{0}" in Zeile "{1}" - - The communication protocol '{0}' is not supported - Das Kommunikationsprotokoll "{0}" wird nicht unterstützt - - [ServerTestHost.OnTaskSchedulerUnobservedTaskException] Unhandled exception: {0} [ServerTestHost.OnTaskSchedulerUnobservedTaskException] Ausnahmefehler: {0} @@ -972,6 +987,11 @@ Gültige Werte sind „Normal“, „Detailed“. Der Standardwert ist „Normal Es wurden keine Tests ausgeführt. + + total + total + + \ 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 a264892a13..8d3a675a7a 100644 --- a/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.es.xlf +++ b/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.es.xlf @@ -224,6 +224,11 @@ Detección de pruebas de + + duration + duration + + Provider '{0}' (UID: {1}) failed with error: {2} Error del proveedor "{0}" (UID: {1}) con el error: {2} @@ -250,8 +255,8 @@ - Another extension with same the same UID '{0}' has already been registered. Registered extension is of type '{1}' - Ya se registró otra extensión con el mismo UID "{0}". La extensión registrada es de tipo "{1}" + Extensions with the same UID '{0}' have already been registered. Registered extensions are of types: {1} + Ya se han registrado extensiones con el mismo UID ''{0}". Las extensiones registradas son de los siguientes tipos: {1} @@ -431,6 +436,11 @@ No se encontró + + Passing both '--treenode-filter' and '--filter-uid' is unsupported. + No se admite el paso de "--treenode-filter" y "--filter-uid". + + Out of process file artifacts produced: Artefactos de archivo fuera de proceso producidos: @@ -461,6 +471,11 @@ Especifica un archivo testconfig.json. + + Allows to pause execution in order to attach to the process for debug purposes. + Permite pausar la ejecución para asociarla al proceso con fines de depuración. + + 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). @@ -533,6 +548,11 @@ Los valores disponibles son 'Seguimiento', 'Depurar', 'Información', 'Advertenc '--{0}' espera un único argumento PID entero + + Provides a list of test node UIDs to filter by. + Proporciona una lista de UID de nodo de prueba por los que filtrar. + + Show the command line help. Muestre la ayuda de la línea de comandos. @@ -733,11 +753,6 @@ Toma un argumento como cadena con el formato <value>[h|m|s] donde 'value' Salida estándar - - Starting server. Listening on port '{0}' - Iniciando el servidor. Escuchando en el puerto '{0}' - - Starting test session. Iniciando sesión de prueba. @@ -748,6 +763,11 @@ Toma un argumento como cadena con el formato <value>[h|m|s] donde 'value' Iniciando sesión de prueba. La ruta de acceso del archivo de registro es '{0}'. + + succeeded + succeeded + + An 'ITestExecutionFilterFactory' factory is already set Ya se ha establecido una fábrica "ITestExecutionFilterFactory" @@ -952,11 +972,6 @@ Los valores válidos son 'Normal', 'Detallado'. El valor predeterminado es 'Norm Estado inesperado en el archivo “{0}” en la línea “{1}” - - The communication protocol '{0}' is not supported - No se admite el protocolo de comunicación '{0}' - - [ServerTestHost.OnTaskSchedulerUnobservedTaskException] Unhandled exception: {0} [ServerTestHost.OnTaskSchedulerUnobservedTaskException] excepción no controlada: {0} @@ -972,6 +987,11 @@ Los valores válidos son 'Normal', 'Detallado'. El valor predeterminado es 'Norm No se ejecutaron pruebas + + total + total + + \ 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 6c08b3f8dc..2c6d00578a 100644 --- a/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.fr.xlf +++ b/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.fr.xlf @@ -224,6 +224,11 @@ Découverte des tests à partir de + + duration + duration + + Provider '{0}' (UID: {1}) failed with error: {2} Désolé, échec de l’« {0} » du fournisseur (UID : {1}) avec l’erreur : {2} @@ -250,8 +255,8 @@ - Another extension with same the same UID '{0}' has already been registered. Registered extension is of type '{1}' - 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} » + Extensions with the same UID '{0}' have already been registered. Registered extensions are of types: {1} + Les extensions ayant le même UID « {0} » ont déjà été enregistrées. Les extensions enregistrées sont de types : {1} @@ -431,6 +436,11 @@ Introuvable + + Passing both '--treenode-filter' and '--filter-uid' is unsupported. + Vous ne pouvez pas passer à la fois « --treenode-filter » et « --filter-uid ». Cette action n’est pas prise en charge. + + Out of process file artifacts produced: Artefacts de fichier hors processus produits : @@ -461,6 +471,11 @@ Spécifie un fichier testconfig.json. + + Allows to pause execution in order to attach to the process for debug purposes. + Permet de suspendre l’exécution afin de s’attacher au processus à des fins de débogage. + + 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). @@ -533,6 +548,11 @@ Les valeurs disponibles sont « Trace », « Debug », « Information », « --{0} » attend un seul argument PID int + + Provides a list of test node UIDs to filter by. + Fournit la liste des IUD de nœud de test à filtrer. + + Show the command line help. Afficher l’aide de la ligne de commande. @@ -733,11 +753,6 @@ Prend un argument sous forme de chaîne au format <value>[h|m|s] où « v Sortie standard - - Starting server. Listening on port '{0}' - Démarrage du serveur. Écoute sur le port « {0} » - - Starting test session. Démarrage de la session de test. @@ -748,6 +763,11 @@ Prend un argument sous forme de chaîne au format <value>[h|m|s] où « v Démarrage de la session de test. Le chemin d’accès au fichier journal est '{0}'. + + succeeded + succeeded + + An 'ITestExecutionFilterFactory' factory is already set Désolé, une fabrique « ITestExecutionFilterFactory » est déjà définie @@ -952,11 +972,6 @@ Les valeurs valides sont « Normal » et « Détaillé ». La valeur par dé État inattendu dans le fichier « {0} » à la ligne « {1} » - - The communication protocol '{0}' is not supported - Le protocole de communication « {0} » n’est pas pris en charge - - [ServerTestHost.OnTaskSchedulerUnobservedTaskException] Unhandled exception: {0} [ServerTestHost.OnTaskSchedulerUnobservedTaskException] exception non prise en charge : {0} @@ -972,6 +987,11 @@ Les valeurs valides sont « Normal » et « Détaillé ». La valeur par dé Zéro tests exécutés + + total + total + + \ 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 bb1706795d..0e04d645af 100644 --- a/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.it.xlf +++ b/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.it.xlf @@ -224,6 +224,11 @@ Individuazione di test da + + duration + duration + + Provider '{0}' (UID: {1}) failed with error: {2} Provider '{0}' (UID: {1}) non riuscito con errore: {2} @@ -250,8 +255,8 @@ - Another extension with same the same UID '{0}' has already been registered. Registered extension is of type '{1}' - È già stata registrata un'altra estensione con lo stesso UID '{0}'. L'estensione registrata è di tipo '{1}' + Extensions with the same UID '{0}' have already been registered. Registered extensions are of types: {1} + Le estensioni con lo stesso UID '{0}' sono già state registrate. Le estensioni registrate sono di tipo: {1} @@ -431,6 +436,11 @@ Non trovato + + Passing both '--treenode-filter' and '--filter-uid' is unsupported. + Il passaggio di '--treenode-filter' e '--filter-uid' non è supportato. + + Out of process file artifacts produced: Artefatti file non in elaborazione prodotti: @@ -461,6 +471,11 @@ Specifica un file testconfig.json. + + Allows to pause execution in order to attach to the process for debug purposes. + Consente di sospendere l'esecuzione per connettersi al processo a scopo di debug. + + 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). @@ -533,6 +548,11 @@ I valori disponibili sono 'Trace', 'Debug', 'Information', 'Warning', 'Error' e '--{0}' prevede un singolo argomento PID int + + Provides a list of test node UIDs to filter by. + Fornisce un elenco di UID dei nodi di prova per il filtraggio. + + Show the command line help. Mostra la Guida della riga di comando. @@ -733,11 +753,6 @@ Acquisisce un argomento come stringa nel formato <value>[h|m|s] dove 'valu Output standard - - Starting server. Listening on port '{0}' - Avvio del server. In ascolto sulla porta '{0}' - - Starting test session. Avvio della sessione di test. @@ -748,6 +763,11 @@ Acquisisce un argomento come stringa nel formato <value>[h|m|s] dove 'valu Avvio della sessione di test. Il percorso del file di log è '{0}'. + + succeeded + succeeded + + An 'ITestExecutionFilterFactory' factory is already set È già impostata una factory 'ITestExecutionFilterFactory' @@ -952,11 +972,6 @@ I valori validi sono 'Normal', 'Detailed'. L'impostazione predefinita è 'Normal Stato imprevisto nel file '{0}' alla riga '{1}' - - The communication protocol '{0}' is not supported - Il protocollo di comunicazione '{0}' non è supportato - - [ServerTestHost.OnTaskSchedulerUnobservedTaskException] Unhandled exception: {0} [ServerTestHost.OnTaskSchedulerUnobservedTaskException] eccezione non gestita: {0} @@ -972,6 +987,11 @@ I valori validi sono 'Normal', 'Detailed'. L'impostazione predefinita è 'Normal Nessun test eseguito + + total + total + + \ 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 825b4cc593..2e95587b6a 100644 --- a/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.ja.xlf +++ b/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.ja.xlf @@ -224,6 +224,11 @@ からテストを検出しています + + duration + duration + + Provider '{0}' (UID: {1}) failed with error: {2} プロバイダー '{0}' (UID: {1}) が次のエラーで失敗しました: {2} @@ -250,8 +255,8 @@ - Another extension with same the same UID '{0}' has already been registered. Registered extension is of type '{1}' - 同じ UID '{0}' を持つ別の拡張機能が既に登録されています。登録されている拡張機能の種類は '{1}' です + Extensions with the same UID '{0}' have already been registered. Registered extensions are of types: {1} + 同じ UID '{0}' の拡張機能は既に登録されています。登録済みの拡張機能の種類: {1} @@ -431,6 +436,11 @@ 見つかりません + + Passing both '--treenode-filter' and '--filter-uid' is unsupported. + '--treenode-filter' と '--filter-uid' の両方を渡すことはサポートされていません。 + + Out of process file artifacts produced: アウトプロセスのファイル成果物が生成されました: @@ -461,6 +471,11 @@ testconfig.json ファイルを指定します。 + + Allows to pause execution in order to attach to the process for debug purposes. + デバッグ目的でプロセスにアタッチするために、実行を一時停止することができます。 + + 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). @@ -534,6 +549,11 @@ The available values are 'Trace', 'Debug', 'Information', 'Warning', 'Error', an '--{0}' には 1 つの int PID 引数が必要です + + Provides a list of test node UIDs to filter by. + フィルター対象のテスト ノード UID のリストを提供します。 + + Show the command line help. コマンド ラインヘルプを表示します。 @@ -734,11 +754,6 @@ Takes one argument as string in the format <value>[h|m|s] where 'value' is 標準出力 - - Starting server. Listening on port '{0}' - サーバーを起動しています。ポート '{0}' で聞いています - - Starting test session. テスト セッションを開始しています。 @@ -749,6 +764,11 @@ Takes one argument as string in the format <value>[h|m|s] where 'value' is テスト セッションを開始しています。ログ ファイルのパスが '{0}' です。 + + succeeded + succeeded + + An 'ITestExecutionFilterFactory' factory is already set 'ITestExecutionFilterFactory' ファクトリは既に設定されています @@ -953,11 +973,6 @@ Valid values are 'Normal', 'Detailed'. Default is 'Normal'. ファイル '{0}' の行 '{1}' の予期しない状態 - - The communication protocol '{0}' is not supported - 通信プロトコル '{0}' はサポートされていません - - [ServerTestHost.OnTaskSchedulerUnobservedTaskException] Unhandled exception: {0} ハンドルされない例外 [ServerTestHost.OnTaskSchedulerUnobservedTaskException]: {0} @@ -973,6 +988,11 @@ Valid values are 'Normal', 'Detailed'. Default is 'Normal'. 0 件のテストが実行されました + + total + total + + \ 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 5b3fd3add9..3a2b31822d 100644 --- a/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.ko.xlf +++ b/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.ko.xlf @@ -224,6 +224,11 @@ 다음에서 테스트 검색하는 중 + + duration + duration + + Provider '{0}' (UID: {1}) failed with error: {2} 공급자 '{0}'(UID: {1})이 오류 {2}(으)로 실패했습니다. @@ -250,8 +255,8 @@ - Another extension with same the same UID '{0}' has already been registered. Registered extension is of type '{1}' - UID '{0}'과(와) 동일한 다른 확장이 이미 등록되었습니다. 등록된 확장은 '{1}' 형식임 + Extensions with the same UID '{0}' have already been registered. Registered extensions are of types: {1} + UID가 '{0}’인 확장이 이미 등록되었습니다. 등록된 확장은 다음과 같은 형식입니다. {1} @@ -431,6 +436,11 @@ 찾을 수 없음 + + Passing both '--treenode-filter' and '--filter-uid' is unsupported. + '--treenode-filter'와 '--filter-uid'를 동시에 전달하는 것은 지원되지 않습니다. + + Out of process file artifacts produced: 생성된 Out of process 파일 아티팩트: @@ -461,6 +471,11 @@ testconfig.json 파일을 지정합니다. + + Allows to pause execution in order to attach to the process for debug purposes. + 디버그 목적으로 프로세스에 연결하기 위해 실행을 일시 중지할 수 있습니다. + + 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). @@ -533,6 +548,11 @@ The available values are 'Trace', 'Debug', 'Information', 'Warning', 'Error', an '--{0}'에는 단일 int PID 인수가 필요합니다. + + Provides a list of test node UIDs to filter by. + 필터링에 사용할 테스트 노드 UID 목록을 제공합니다. + + Show the command line help. 명령줄 도움말을 표시합니다. @@ -733,11 +753,6 @@ Takes one argument as string in the format <value>[h|m|s] where 'value' is 표준 출력 - - Starting server. Listening on port '{0}' - 서버를 시작하는 중입니다. 포트 '{0}'에서 수신 대기 중 - - Starting test session. 테스트 세션을 시작하는 중입니다. @@ -748,6 +763,11 @@ Takes one argument as string in the format <value>[h|m|s] where 'value' is 테스트 세션을 시작하는 중입니다. 로그 파일 경로가 '{0}'. + + succeeded + succeeded + + An 'ITestExecutionFilterFactory' factory is already set 'ITestExecutionFilterFactory' 팩터리가 이미 설정되어 있음 @@ -952,11 +972,6 @@ Valid values are 'Normal', 'Detailed'. Default is 'Normal'. '{1}' 줄의 '{0}' 파일에 예기치 않은 상태가 있습니다. - - The communication protocol '{0}' is not supported - 통신 프로토콜 '{0}'은(는) 지원되지 않습니다. - - [ServerTestHost.OnTaskSchedulerUnobservedTaskException] Unhandled exception: {0} 처리되지 않은 예외 [ServerTestHost.OnTaskSchedulerUnobservedTaskException]: {0} @@ -972,6 +987,11 @@ Valid values are 'Normal', 'Detailed'. Default is 'Normal'. 테스트가 0개 실행됨 + + total + total + + \ 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 4f723bdd74..7421f6eef8 100644 --- a/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.pl.xlf +++ b/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.pl.xlf @@ -224,6 +224,11 @@ Odnajdywanie testów w + + duration + duration + + Provider '{0}' (UID: {1}) failed with error: {2} Dostawca „{0}” (UID:{1}) nie powiódł się z powodu błędu: {2} @@ -250,8 +255,8 @@ - Another extension with same the same UID '{0}' has already been registered. Registered extension is of type '{1}' - Inne rozszerzenie o tym samym identyfikatorze UID „{0}” zostało już zarejestrowane. Zarejestrowane rozszerzenie jest typu „{1}” + Extensions with the same UID '{0}' have already been registered. Registered extensions are of types: {1} + Rozszerzenia o tym samym identyfikatorze UID „{0}” zostały już zarejestrowane. Zarejestrowane rozszerzenia są typu: {1} @@ -431,6 +436,11 @@ Nie znaleziono + + Passing both '--treenode-filter' and '--filter-uid' is unsupported. + Przekazywanie obu parametrów „--treenode-filter” i „--filter-uid” jest nieobsługiwane. + + Out of process file artifacts produced: Wygenerowane artefakty pliku poza procesem: @@ -461,6 +471,11 @@ Określa plik testconfig.json. + + Allows to pause execution in order to attach to the process for debug purposes. + Umożliwia wstrzymanie wykonywania w celu dołączenia do procesu na potrzeby debugowania. + + 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). @@ -533,6 +548,11 @@ Dostępne wartości to „Trace”, „Debug”, „Information”, „Warning „--{0}” oczekuje pojedynczego argumentu int identyfikatora PID + + Provides a list of test node UIDs to filter by. + Zawiera listę identyfikatorów UID węzła testowego do filtrowania. + + Show the command line help. Pokaż pomoc wiersza polecenia. @@ -733,11 +753,6 @@ Pobiera jeden argument jako ciąg w formacie <value>[h|m|s], gdzie element Standardowe dane wyjściowe - - Starting server. Listening on port '{0}' - Uruchamianie serwera. Nasłuchiwanie na porcie „{0}” - - Starting test session. Rozpoczynanie sesji testowej. @@ -748,6 +763,11 @@ Pobiera jeden argument jako ciąg w formacie <value>[h|m|s], gdzie element Rozpoczynanie sesji testowej. Ścieżka pliku dziennika jest '{0}'. + + succeeded + succeeded + + An 'ITestExecutionFilterFactory' factory is already set Fabryka „ITestExecutionFilterFactory” jest już ustawiona @@ -952,11 +972,6 @@ Prawidłowe wartości to „Normalne”, „Szczegółowe”. Wartość domyśln Nieoczekiwany stan w pliku „{0}” w wierszu „{1}” - - The communication protocol '{0}' is not supported - Protokół komunikacyjny „{0}” nie jest obsługiwany - - [ServerTestHost.OnTaskSchedulerUnobservedTaskException] Unhandled exception: {0} [ServerTestHost.OnTaskSchedulerUnobservedTaskException] Nieobsługiwany wyjątek: {0} @@ -972,6 +987,11 @@ Prawidłowe wartości to „Normalne”, „Szczegółowe”. Wartość domyśln Uruchomiono zero testów + + total + total + + \ 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 df330a27f2..0d484a22bf 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 @@ -224,6 +224,11 @@ Descobrindo testes de + + duration + duration + + Provider '{0}' (UID: {1}) failed with error: {2} O provedor ''{0}'' (UID: {1}) falhou com o erro: {2} @@ -250,8 +255,8 @@ - Another extension with same the same UID '{0}' has already been registered. Registered extension is of type '{1}' - Outra extensão com o mesmo UID “{0}” já foi registrada. A extensão registrada é do tipo “{1}” + Extensions with the same UID '{0}' have already been registered. Registered extensions are of types: {1} + Extensões com o mesmo UID '{0}' já foram registradas. As extensões registradas são dos tipos: {1} @@ -431,6 +436,11 @@ Não encontrado + + Passing both '--treenode-filter' and '--filter-uid' is unsupported. + Não há suporte para a passagem de "--treenode-filter" e "--filter-uid". + + Out of process file artifacts produced: Artefatos de arquivo fora do processo produzidos: @@ -461,6 +471,11 @@ Especifica um arquivo testconfig.json. + + Allows to pause execution in order to attach to the process for debug purposes. + Permite pausar a execução a fim de anexar ao processo para fins de depuração. + + 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). @@ -533,6 +548,11 @@ Os valores disponíveis são 'Rastreamento', 'Depuração', 'Informação', 'Avi "--{0}" espera um único argumento int PID + + Provides a list of test node UIDs to filter by. + Fornece uma lista de UIDs de nó de teste para filtrar. + + Show the command line help. Mostrar a ajuda da linha de comando. @@ -733,11 +753,6 @@ Recebe um argumento como cadeia de caracteres no formato <valor>[h|m|s] em Saída padrão - - Starting server. Listening on port '{0}' - Iniciando servidor. Escutando na porta “{0}” - - Starting test session. Iniciando sessão de teste. @@ -748,6 +763,11 @@ Recebe um argumento como cadeia de caracteres no formato <valor>[h|m|s] em Iniciando sessão de teste. O caminho do arquivo de log '{0}'. + + succeeded + succeeded + + An 'ITestExecutionFilterFactory' factory is already set Uma fábrica “ITestExecutionFilterFactory” já está definida @@ -952,11 +972,6 @@ Os valores válidos são “Normal”, “Detalhado”. O padrão é “Normal Estado inesperado no arquivo '{0}' na linha '{1}' - - The communication protocol '{0}' is not supported - O protocolo de comunicação “{0}” não tem suporte - - [ServerTestHost.OnTaskSchedulerUnobservedTaskException] Unhandled exception: {0} [ServerTestHost.OnTaskSchedulerUnobservedTaskException] exceção sem tratamento: {0} @@ -972,6 +987,11 @@ Os valores válidos são “Normal”, “Detalhado”. O padrão é “Normal Zero testes executados + + total + total + + \ 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 85478d870f..73a4cf88ab 100644 --- a/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.ru.xlf +++ b/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.ru.xlf @@ -224,6 +224,11 @@ Обнаружение тестов из + + duration + duration + + Provider '{0}' (UID: {1}) failed with error: {2} Сбой поставщика "{0}" (ИД пользователя: {1}) с ошибкой: {2} @@ -250,8 +255,8 @@ - Another extension with same the same UID '{0}' has already been registered. Registered extension is of type '{1}' - Уже зарегистрировано другое расширение с таким же ИД пользователя "{0}". Зарегистрированное расширение относится к типу "{1}" + Extensions with the same UID '{0}' have already been registered. Registered extensions are of types: {1} + Расширения с тем же UID "{0}" уже зарегистрированы. Зарегистрированные расширения относятся к следующим типам: {1} @@ -431,6 +436,11 @@ Не найдено + + Passing both '--treenode-filter' and '--filter-uid' is unsupported. + Передача одновременно "--treenode-filter" и "--filter-uid" не поддерживается. + + Out of process file artifacts produced: Созданные вне процесса артефакты файлов: @@ -461,6 +471,11 @@ Указывает файл testconfig.json. + + Allows to pause execution in order to attach to the process for debug purposes. + Позволяет приостановить выполнение, чтобы подключиться к процессу для отладки. + + 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). @@ -533,6 +548,11 @@ The available values are 'Trace', 'Debug', 'Information', 'Warning', 'Error', an "--{0}" ожидает один аргумент int PID + + Provides a list of test node UIDs to filter by. + Предоставляет список UID тестовых узлов для фильтрации. + + Show the command line help. Показать справку по командной строке. @@ -733,11 +753,6 @@ Takes one argument as string in the format <value>[h|m|s] where 'value' is Стандартный вывод - - Starting server. Listening on port '{0}' - Выполняется запуск сервера. Прослушивание порта "{0}" - - Starting test session. Запуск тестового сеанса. @@ -748,6 +763,11 @@ Takes one argument as string in the format <value>[h|m|s] where 'value' is Запуск тестового сеанса. Путь к файлу журнала '{0}'. + + succeeded + succeeded + + An 'ITestExecutionFilterFactory' factory is already set Фабрика ITestExecutionFilterFactory уже настроена @@ -952,11 +972,6 @@ Valid values are 'Normal', 'Detailed'. Default is 'Normal'. Непредвиденное состояние в файле "{0}" в строке "{1}" - - The communication protocol '{0}' is not supported - Протокол коммуникации "{0}" не поддерживается - - [ServerTestHost.OnTaskSchedulerUnobservedTaskException] Unhandled exception: {0} [ServerTestHost.OnTaskSchedulerUnobservedTaskException] необработанное исключение: {0} @@ -972,6 +987,11 @@ Valid values are 'Normal', 'Detailed'. Default is 'Normal'. Запущено ноль тестов + + total + total + + \ 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 4c7c2fbd6c..a0662f9594 100644 --- a/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.tr.xlf +++ b/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.tr.xlf @@ -224,6 +224,11 @@ Testler şuradan bulunuyor: + + duration + duration + + Provider '{0}' (UID: {1}) failed with error: {2} '{0}' sağlayıcısı '' (UID: {1}) şu hatayla başarısız oldu: {2} @@ -250,8 +255,8 @@ - Another extension with same the same UID '{0}' has already been registered. Registered extension is of type '{1}' - Aynı UID '{0}' olan başka bir uzantı zaten kayıtlı. Kayıtlı uzantı '{1}' türünde + Extensions with the same UID '{0}' have already been registered. Registered extensions are of types: {1} + Aynı UID ‘{0}’ ile uzantılar zaten kayıtlıdır. Kayıtlı uzantılar şu türlerde olabilir: {1} @@ -431,6 +436,11 @@ Bulunamadı + + Passing both '--treenode-filter' and '--filter-uid' is unsupported. + Hem ‘--treenode-filter’ hem de ‘--filter-uid’ parametrelerinin birlikte kullanılması desteklenmemektedir. + + Out of process file artifacts produced: Üretilen işlem dışı dosya yapıtları: @@ -461,6 +471,11 @@ testconfig.json dosyası belirtir. + + Allows to pause execution in order to attach to the process for debug purposes. + Hata ayıklama amacıyla işleme eklemek için yürütmeyi duraklatmayı sağlar. + + 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). @@ -533,6 +548,11 @@ Kullanılabilir değerler: 'Trace', 'Debug', 'Information', 'Warning', 'Error' v '--{0}', tek bir int PID bağımsız değişkeni bekliyor + + Provides a list of test node UIDs to filter by. + Filtrelemek için test düğümü UID'lerinin bir listesini sağlar. + + Show the command line help. Komut satırı yardımını gösterir. @@ -733,11 +753,6 @@ Bir bağımsız değişkeni, 'value' değerinin kayan olduğu <value>[h|m| Standart çıkış - - Starting server. Listening on port '{0}' - Sunucu başlatılıyor. '{0}' bağlantı noktasında dinleme işlemi yapılıyor - - Starting test session. Test oturumu başlatılıyor. @@ -748,6 +763,11 @@ Bir bağımsız değişkeni, 'value' değerinin kayan olduğu <value>[h|m| Test oturumu başlatılıyor. Günlük dosyası yolu '{0}'. + + succeeded + succeeded + + An 'ITestExecutionFilterFactory' factory is already set Bir 'ITestExecutionFilterFactory' fabrikası zaten ayarlanmış @@ -952,11 +972,6 @@ Geçerli değerler: ‘Normal’, ‘Ayrıntılı’. Varsayılan değer: ‘Nor '{0}' dosyasında '{1}' satırındaki durum beklenmiyordu - - The communication protocol '{0}' is not supported - '{0}' iletişim protokolü desteklenmiyor - - [ServerTestHost.OnTaskSchedulerUnobservedTaskException] Unhandled exception: {0} [ServerTestHost.OnTaskSchedulerUnobservedTaskException] özel durum: {0} @@ -972,6 +987,11 @@ Geçerli değerler: ‘Normal’, ‘Ayrıntılı’. Varsayılan değer: ‘Nor Sıfır test çalıştırıldı + + total + total + + \ 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 15741f97e4..4303cd2fbd 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 @@ -224,6 +224,11 @@ 正在发现以下位置中的测试 + + duration + duration + + Provider '{0}' (UID: {1}) failed with error: {2} 提供程序 '{0}' (UID: {1}) 失败,出现错误: {2} @@ -250,8 +255,8 @@ - Another extension with same the same UID '{0}' has already been registered. Registered extension is of type '{1}' - 已注册另一个具有相同 UID“{0}”的扩展。已注册的扩展属于“{1}”类型 + Extensions with the same UID '{0}' have already been registered. Registered extensions are of types: {1} + 已注册具有相同 UID“{0}”的扩展。已注册的扩展类型如下: {1} @@ -431,6 +436,11 @@ 未找到 + + Passing both '--treenode-filter' and '--filter-uid' is unsupported. + 不支持同时传递“--treenode-filter”和“--filter-uid”。 + + Out of process file artifacts produced: 生成的进程外文件项目: @@ -461,6 +471,11 @@ 指定 testconfig.json 文件。 + + Allows to pause execution in order to attach to the process for debug purposes. + 允许暂停执行,以便附加到进程以进行调试。 + + 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). @@ -533,6 +548,11 @@ The available values are 'Trace', 'Debug', 'Information', 'Warning', 'Error', an “--{0}”需要单个 int PID 参数 + + Provides a list of test node UIDs to filter by. + 提供用作筛选依据的测试节点 UID 列表。 + + Show the command line help. 显示命令行帮助。 @@ -733,11 +753,6 @@ Takes one argument as string in the format <value>[h|m|s] where 'value' is 标准输出 - - Starting server. Listening on port '{0}' - 正在启动服务器。正在侦听端口“{0}” - - Starting test session. 正在启动测试会话。 @@ -748,6 +763,11 @@ Takes one argument as string in the format <value>[h|m|s] where 'value' is 正在启动测试会话。日志文件路径 '{0}'。 + + succeeded + succeeded + + An 'ITestExecutionFilterFactory' factory is already set 已设置“ITestExecutionFilterFactory”工厂 @@ -952,11 +972,6 @@ Valid values are 'Normal', 'Detailed'. Default is 'Normal'. 文件“{0}”中第“{1}”行出现意外状态 - - The communication protocol '{0}' is not supported - 不支持通信协议“{0}” - - [ServerTestHost.OnTaskSchedulerUnobservedTaskException] Unhandled exception: {0} [ServerTestHost.OnTaskSchedulerUnobservedTaskException] 未经处理的异常: {0} @@ -972,6 +987,11 @@ Valid values are 'Normal', 'Detailed'. Default is 'Normal'. 运行了零个测试 + + total + total + + \ 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 a788e45f92..8998bd0ac4 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 @@ -224,6 +224,11 @@ 正在以下位置找測試 + + duration + duration + + Provider '{0}' (UID: {1}) failed with error: {2} 提供者 '{0}' (UID: {1}) 失敗,發生錯誤: {2} @@ -250,8 +255,8 @@ - Another extension with same the same UID '{0}' has already been registered. Registered extension is of type '{1}' - 已註冊另一個具有相同 UID '{0}' 的延伸模組。已註冊的延伸模組屬於 '{1}' 類型 + Extensions with the same UID '{0}' have already been registered. Registered extensions are of types: {1} + 具有相同 UID '{0}' 的延伸模組已經註冊。已註冊的延伸模組類型為: {1} @@ -431,6 +436,11 @@ 找不到 + + Passing both '--treenode-filter' and '--filter-uid' is unsupported. + 不支援同時傳遞 '--treenode-filter' 和 '--filter-uid'。 + + Out of process file artifacts produced: 產生的流程外檔案成品: @@ -461,6 +471,11 @@ 指定 testconfig.json 檔案。 + + Allows to pause execution in order to attach to the process for debug purposes. + 允許暫停執行,以便附加至處理常式以供偵錯之用。 + + 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). @@ -533,6 +548,11 @@ The available values are 'Trace', 'Debug', 'Information', 'Warning', 'Error', an '--{0}' 需要單一 int PID 引數 + + Provides a list of test node UIDs to filter by. + 提供要篩選的測試節點 UID 清單。 + + Show the command line help. 顯示命令列說明。 @@ -733,11 +753,6 @@ Takes one argument as string in the format <value>[h|m|s] where 'value' is 標準輸出 - - Starting server. Listening on port '{0}' - 正在啟動伺服器。正在連接埠 '{0}' 上聆聽 - - Starting test session. 正在啟動測試會話。 @@ -748,6 +763,11 @@ Takes one argument as string in the format <value>[h|m|s] where 'value' is 正在啟動測試會話。記錄檔路徑 '{0}'。 + + succeeded + succeeded + + An 'ITestExecutionFilterFactory' factory is already set 已設定 'ITestExecutionFilterFactory' 中心 @@ -952,11 +972,6 @@ Valid values are 'Normal', 'Detailed'. Default is 'Normal'. 檔案 '{0}' 的第 '{1}' 行出現未預期的狀態 - - The communication protocol '{0}' is not supported - 通訊協定 '{0}' 不受支援 - - [ServerTestHost.OnTaskSchedulerUnobservedTaskException] Unhandled exception: {0} [ServerTestHost.OnTaskSchedulerUnobservedTaskException] 未處理的例外狀況: {0} @@ -972,6 +987,11 @@ Valid values are 'Normal', 'Detailed'. Default is 'Normal'. 已執行零項測試 + + total + total + + \ No newline at end of file diff --git a/src/Platform/Microsoft.Testing.Platform/ServerMode/DotnetTest/DotnetTestConnection.cs b/src/Platform/Microsoft.Testing.Platform/ServerMode/DotnetTest/DotnetTestConnection.cs index 8b1d908167..dc9cc2902b 100644 --- a/src/Platform/Microsoft.Testing.Platform/ServerMode/DotnetTest/DotnetTestConnection.cs +++ b/src/Platform/Microsoft.Testing.Platform/ServerMode/DotnetTest/DotnetTestConnection.cs @@ -20,7 +20,6 @@ internal sealed class DotnetTestConnection : IPushOnlyProtocol, IDisposable { private readonly CommandLineHandler _commandLineHandler; - private readonly IProcessHandler _processHandler; private readonly IEnvironment _environment; private readonly ITestApplicationModuleInfo _testApplicationModuleInfo; private readonly ITestApplicationCancellationTokenSource _cancellationTokenSource; @@ -29,10 +28,9 @@ internal sealed class DotnetTestConnection : IPushOnlyProtocol, public static string InstanceId { get; } = Guid.NewGuid().ToString("N"); - public DotnetTestConnection(CommandLineHandler commandLineHandler, IProcessHandler processHandler, IEnvironment environment, ITestApplicationModuleInfo testApplicationModuleInfo, ITestApplicationCancellationTokenSource cancellationTokenSource) + public DotnetTestConnection(CommandLineHandler commandLineHandler, IEnvironment environment, ITestApplicationModuleInfo testApplicationModuleInfo, ITestApplicationCancellationTokenSource cancellationTokenSource) { _commandLineHandler = commandLineHandler; - _processHandler = processHandler; _environment = environment; _testApplicationModuleInfo = testApplicationModuleInfo; _cancellationTokenSource = cancellationTokenSource; @@ -58,10 +56,10 @@ public async Task AfterCommonServiceSetupAsync() _environment.SetEnvironmentVariable(EnvironmentVariableConstants.TESTINGPLATFORM_DOTNETTEST_EXECUTIONID, Guid.NewGuid().ToString("N")); } - _dotnetTestPipeClient = new(arguments[0]); + _dotnetTestPipeClient = new(arguments[0], _environment); _dotnetTestPipeClient.RegisterAllSerializers(); - await _dotnetTestPipeClient.ConnectAsync(_cancellationTokenSource.CancellationToken); + await _dotnetTestPipeClient.ConnectAsync(_cancellationTokenSource.CancellationToken).ConfigureAwait(false); } } @@ -69,7 +67,7 @@ public async Task HelpInvokedAsync() { RoslynDebug.Assert(_dotnetTestPipeClient is not null); - List commandLineHelpOptions = new(); + List commandLineHelpOptions = []; foreach (ICommandLineOptionsProvider commandLineOptionProvider in _commandLineHandler.CommandLineOptionsProviders) { if (commandLineOptionProvider is IToolCommandLineOptionsProvider) @@ -87,9 +85,11 @@ public async Task HelpInvokedAsync() } } - await _dotnetTestPipeClient.RequestReplyAsync(new CommandLineOptionMessages(_testApplicationModuleInfo.GetCurrentTestApplicationFullPath(), commandLineHelpOptions.OrderBy(option => option.Name).ToArray()), _cancellationTokenSource.CancellationToken); + await _dotnetTestPipeClient.RequestReplyAsync(new CommandLineOptionMessages(_testApplicationModuleInfo.GetCurrentTestApplicationFullPath(), [.. commandLineHelpOptions.OrderBy(option => option.Name)]), _cancellationTokenSource.CancellationToken).ConfigureAwait(false); } + public bool IsIDE { get; private set; } + public async Task IsCompatibleProtocolAsync(string hostType) { RoslynDebug.Assert(_dotnetTestPipeClient is not null); @@ -97,7 +97,7 @@ public async Task IsCompatibleProtocolAsync(string hostType) string supportedProtocolVersions = ProtocolConstants.Version; HandshakeMessage handshakeMessage = new(new Dictionary { - { HandshakeMessagePropertyNames.PID, _processHandler.GetCurrentProcess().Id.ToString(CultureInfo.InvariantCulture) }, + { HandshakeMessagePropertyNames.PID, _environment.ProcessId.ToString(CultureInfo.InvariantCulture) }, { HandshakeMessagePropertyNames.Architecture, RuntimeInformation.ProcessArchitecture.ToString() }, { HandshakeMessagePropertyNames.Framework, RuntimeInformation.FrameworkDescription }, { HandshakeMessagePropertyNames.OS, RuntimeInformation.OSDescription }, @@ -108,7 +108,11 @@ public async Task IsCompatibleProtocolAsync(string hostType) { HandshakeMessagePropertyNames.InstanceId, InstanceId }, }); - HandshakeMessage response = await _dotnetTestPipeClient.RequestReplyAsync(handshakeMessage, _cancellationTokenSource.CancellationToken); + HandshakeMessage response = await _dotnetTestPipeClient.RequestReplyAsync(handshakeMessage, _cancellationTokenSource.CancellationToken).ConfigureAwait(false); + + IsIDE = response.Properties?.TryGetValue(HandshakeMessagePropertyNames.IsIDE, out string? isIDEValue) == true && + bool.TryParse(isIDEValue, out bool isIDE) && + isIDE; return response.Properties?.TryGetValue(HandshakeMessagePropertyNames.SupportedProtocolVersions, out string? protocolVersion) == true && IsVersionCompatible(protocolVersion, supportedProtocolVersions); @@ -123,19 +127,19 @@ public async Task SendMessageAsync(IRequest message) switch (message) { case DiscoveredTestMessages discoveredTestMessages: - await _dotnetTestPipeClient.RequestReplyAsync(discoveredTestMessages, _cancellationTokenSource.CancellationToken); + await _dotnetTestPipeClient.RequestReplyAsync(discoveredTestMessages, _cancellationTokenSource.CancellationToken).ConfigureAwait(false); break; case TestResultMessages testResultMessages: - await _dotnetTestPipeClient.RequestReplyAsync(testResultMessages, _cancellationTokenSource.CancellationToken); + await _dotnetTestPipeClient.RequestReplyAsync(testResultMessages, _cancellationTokenSource.CancellationToken).ConfigureAwait(false); break; case FileArtifactMessages fileArtifactMessages: - await _dotnetTestPipeClient.RequestReplyAsync(fileArtifactMessages, _cancellationTokenSource.CancellationToken); + await _dotnetTestPipeClient.RequestReplyAsync(fileArtifactMessages, _cancellationTokenSource.CancellationToken).ConfigureAwait(false); break; case TestSessionEvent testSessionEvent: - await _dotnetTestPipeClient.RequestReplyAsync(testSessionEvent, _cancellationTokenSource.CancellationToken); + await _dotnetTestPipeClient.RequestReplyAsync(testSessionEvent, _cancellationTokenSource.CancellationToken).ConfigureAwait(false); break; } } @@ -149,7 +153,7 @@ public async ValueTask DisposeAsync() { if (_dotnetTestPipeClient is not null) { - await _dotnetTestPipeClient.DisposeAsync(); + await _dotnetTestPipeClient.DisposeAsync().ConfigureAwait(false); } } #endif diff --git a/src/Platform/Microsoft.Testing.Platform/ServerMode/DotnetTest/IPC/Constants.cs b/src/Platform/Microsoft.Testing.Platform/ServerMode/DotnetTest/IPC/Constants.cs index 263abfa2b9..11239b8039 100644 --- a/src/Platform/Microsoft.Testing.Platform/ServerMode/DotnetTest/IPC/Constants.cs +++ b/src/Platform/Microsoft.Testing.Platform/ServerMode/DotnetTest/IPC/Constants.cs @@ -12,6 +12,7 @@ internal static class TestStates internal const byte Error = 4; internal const byte Timeout = 5; internal const byte Cancelled = 6; + internal const byte InProgress = 7; } internal static class SessionEventTypes @@ -31,6 +32,7 @@ internal static class HandshakeMessagePropertyNames internal const byte ModulePath = 6; internal const byte ExecutionId = 7; internal const byte InstanceId = 8; + internal const byte IsIDE = 9; } internal static class ProtocolConstants diff --git a/src/Platform/Microsoft.Testing.Platform/ServerMode/DotnetTest/IPC/DotnetTestDataConsumer.cs b/src/Platform/Microsoft.Testing.Platform/ServerMode/DotnetTest/IPC/DotnetTestDataConsumer.cs index 702a3bd9be..4c0421dea6 100644 --- a/src/Platform/Microsoft.Testing.Platform/ServerMode/DotnetTest/IPC/DotnetTestDataConsumer.cs +++ b/src/Platform/Microsoft.Testing.Platform/ServerMode/DotnetTest/IPC/DotnetTestDataConsumer.cs @@ -21,13 +21,13 @@ public DotnetTestDataConsumer(DotnetTestConnection dotnetTestConnection, IEnviro _environment = environment; } - public Type[] DataTypesConsumed => new[] - { + public Type[] DataTypesConsumed => + [ typeof(TestNodeUpdateMessage), typeof(SessionFileArtifact), typeof(FileArtifact), - typeof(TestRequestExecutionTimeInfo), - }; + typeof(TestRequestExecutionTimeInfo) + ]; public string Uid => nameof(DotnetTestDataConsumer); @@ -56,26 +56,42 @@ public async Task ConsumeAsync(IDataProducer dataProducer, IData value, Cancella switch (testNodeDetails.State) { case TestStates.Discovered: + TestFileLocationProperty? testFileLocationProperty = null; + TestMethodIdentifierProperty? testMethodIdentifierProperty = null; + TestMetadataProperty[] traits = []; + if (_dotnetTestConnection.IsIDE) + { + testFileLocationProperty = testNodeUpdateMessage.TestNode.Properties.SingleOrDefault(); + testMethodIdentifierProperty = testNodeUpdateMessage.TestNode.Properties.SingleOrDefault(); + traits = testNodeUpdateMessage.TestNode.Properties.OfType(); + } + DiscoveredTestMessages discoveredTestMessages = new( ExecutionId, DotnetTestConnection.InstanceId, - new[] - { + [ new DiscoveredTestMessage( testNodeUpdateMessage.TestNode.Uid.Value, - testNodeUpdateMessage.TestNode.DisplayName), - }); - - await _dotnetTestConnection.SendMessageAsync(discoveredTestMessages); + testNodeUpdateMessage.TestNode.DisplayName, + testFileLocationProperty?.FilePath, + testFileLocationProperty?.LineSpan.Start.Line, + testMethodIdentifierProperty?.Namespace, + testMethodIdentifierProperty?.TypeName, + testMethodIdentifierProperty?.MethodName, + testMethodIdentifierProperty?.ParameterTypeFullNames, + traits) + ]); + + await _dotnetTestConnection.SendMessageAsync(discoveredTestMessages).ConfigureAwait(false); break; case TestStates.Passed: case TestStates.Skipped: + case TestStates.InProgress when _dotnetTestConnection.IsIDE: TestResultMessages testResultMessages = new( ExecutionId, DotnetTestConnection.InstanceId, - new[] - { + [ new SuccessfulTestResultMessage( testNodeUpdateMessage.TestNode.Uid.Value, testNodeUpdateMessage.TestNode.DisplayName, @@ -84,11 +100,11 @@ public async Task ConsumeAsync(IDataProducer dataProducer, IData value, Cancella testNodeDetails.Reason ?? string.Empty, testNodeDetails.StandardOutput ?? string.Empty, testNodeDetails.StandardError ?? string.Empty, - testNodeUpdateMessage.SessionUid.Value), - }, - Array.Empty()); + testNodeUpdateMessage.SessionUid.Value) + ], + []); - await _dotnetTestConnection.SendMessageAsync(testResultMessages); + await _dotnetTestConnection.SendMessageAsync(testResultMessages).ConfigureAwait(false); break; case TestStates.Failed: @@ -98,9 +114,8 @@ public async Task ConsumeAsync(IDataProducer dataProducer, IData value, Cancella testResultMessages = new( ExecutionId, DotnetTestConnection.InstanceId, - Array.Empty(), - new[] - { + [], + [ new FailedTestResultMessage( testNodeUpdateMessage.TestNode.Uid.Value, testNodeUpdateMessage.TestNode.DisplayName, @@ -110,10 +125,10 @@ public async Task ConsumeAsync(IDataProducer dataProducer, IData value, Cancella testNodeDetails.Exceptions, testNodeDetails.StandardOutput ?? string.Empty, testNodeDetails.StandardError ?? string.Empty, - testNodeUpdateMessage.SessionUid.Value), - }); + testNodeUpdateMessage.SessionUid.Value) + ]); - await _dotnetTestConnection.SendMessageAsync(testResultMessages); + await _dotnetTestConnection.SendMessageAsync(testResultMessages).ConfigureAwait(false); break; } @@ -122,18 +137,17 @@ public async Task ConsumeAsync(IDataProducer dataProducer, IData value, Cancella FileArtifactMessages testFileArtifactMessages = new( ExecutionId, DotnetTestConnection.InstanceId, - new[] - { - new FileArtifactMessage( - artifact.FileInfo.FullName, - artifact.DisplayName, - artifact.Description ?? string.Empty, - testNodeUpdateMessage.TestNode.Uid.Value, - testNodeUpdateMessage.TestNode.DisplayName, - testNodeUpdateMessage.SessionUid.Value), - }); - - await _dotnetTestConnection.SendMessageAsync(testFileArtifactMessages); + [ + new FileArtifactMessage( + artifact.FileInfo.FullName, + artifact.DisplayName, + artifact.Description ?? string.Empty, + testNodeUpdateMessage.TestNode.Uid.Value, + testNodeUpdateMessage.TestNode.DisplayName, + testNodeUpdateMessage.SessionUid.Value) + ]); + + await _dotnetTestConnection.SendMessageAsync(testFileArtifactMessages).ConfigureAwait(false); } break; @@ -142,36 +156,34 @@ public async Task ConsumeAsync(IDataProducer dataProducer, IData value, Cancella var fileArtifactMessages = new FileArtifactMessages( ExecutionId, DotnetTestConnection.InstanceId, - new[] - { + [ new FileArtifactMessage( sessionFileArtifact.FileInfo.FullName, sessionFileArtifact.DisplayName, sessionFileArtifact.Description ?? string.Empty, string.Empty, string.Empty, - sessionFileArtifact.SessionUid.Value), - }); + sessionFileArtifact.SessionUid.Value) + ]); - await _dotnetTestConnection.SendMessageAsync(fileArtifactMessages); + await _dotnetTestConnection.SendMessageAsync(fileArtifactMessages).ConfigureAwait(false); break; case FileArtifact fileArtifact: fileArtifactMessages = new( ExecutionId, DotnetTestConnection.InstanceId, - new[] - { + [ new FileArtifactMessage( fileArtifact.FileInfo.FullName, fileArtifact.DisplayName, fileArtifact.Description ?? string.Empty, string.Empty, string.Empty, - string.Empty), - }); + string.Empty) + ]); - await _dotnetTestConnection.SendMessageAsync(fileArtifactMessages); + await _dotnetTestConnection.SendMessageAsync(fileArtifactMessages).ConfigureAwait(false); break; } } @@ -235,6 +247,10 @@ public async Task ConsumeAsync(IDataProducer dataProducer, IData value, Cancella reason = nodeState.Explanation; exceptions = FlattenToExceptionMessages(reason, cancelledTestNodeStateProperty.Exception); break; + + case InProgressTestNodeStateProperty: + state = TestStates.InProgress; + break; } return new TestNodeDetails(state, duration, reason, exceptions, standardOutput, standardError); @@ -271,7 +287,7 @@ public async Task OnTestSessionStartingAsync(SessionUid sessionUid, Cancellation sessionUid.Value, ExecutionId); - await _dotnetTestConnection.SendMessageAsync(sessionStartEvent); + await _dotnetTestConnection.SendMessageAsync(sessionStartEvent).ConfigureAwait(false); } public async Task OnTestSessionFinishingAsync(SessionUid sessionUid, CancellationToken cancellationToken) @@ -283,6 +299,6 @@ public async Task OnTestSessionFinishingAsync(SessionUid sessionUid, Cancellatio sessionUid.Value, ExecutionId); - await _dotnetTestConnection.SendMessageAsync(sessionEndEvent); + await _dotnetTestConnection.SendMessageAsync(sessionEndEvent).ConfigureAwait(false); } } diff --git a/src/Platform/Microsoft.Testing.Platform/ServerMode/DotnetTest/IPC/Models/DiscoveredTestMessages.cs b/src/Platform/Microsoft.Testing.Platform/ServerMode/DotnetTest/IPC/Models/DiscoveredTestMessages.cs index 4d83410741..1f2275d1c9 100644 --- a/src/Platform/Microsoft.Testing.Platform/ServerMode/DotnetTest/IPC/Models/DiscoveredTestMessages.cs +++ b/src/Platform/Microsoft.Testing.Platform/ServerMode/DotnetTest/IPC/Models/DiscoveredTestMessages.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 Microsoft.Testing.Platform.Extensions.Messages; + namespace Microsoft.Testing.Platform.IPC.Models; -internal sealed record DiscoveredTestMessage(string? Uid, string? DisplayName); +internal sealed record DiscoveredTestMessage(string? Uid, string? DisplayName, string? FilePath, int? LineNumber, string? Namespace, string? TypeName, string? MethodName, string[]? ParameterTypeFullNames, TestMetadataProperty[] Traits); internal sealed record DiscoveredTestMessages(string? ExecutionId, string? InstanceId, DiscoveredTestMessage[] DiscoveredMessages) : IRequest; diff --git a/src/Platform/Microsoft.Testing.Platform/ServerMode/DotnetTest/IPC/ObjectFieldIds.cs b/src/Platform/Microsoft.Testing.Platform/ServerMode/DotnetTest/IPC/ObjectFieldIds.cs index d052766562..dc61f0c85f 100644 --- a/src/Platform/Microsoft.Testing.Platform/ServerMode/DotnetTest/IPC/ObjectFieldIds.cs +++ b/src/Platform/Microsoft.Testing.Platform/ServerMode/DotnetTest/IPC/ObjectFieldIds.cs @@ -51,6 +51,19 @@ internal static class DiscoveredTestMessageFieldsId { public const ushort Uid = 1; public const ushort DisplayName = 2; + public const ushort FilePath = 3; + public const ushort LineNumber = 4; + public const ushort Namespace = 5; + public const ushort TypeName = 6; + public const ushort MethodName = 7; + public const ushort Traits = 8; + public const ushort ParameterTypeFullNames = 9; +} + +internal static class TraitMessageFieldsId +{ + public const ushort Key = 1; + public const ushort Value = 2; } internal static class TestResultMessagesFieldsId diff --git a/src/Platform/Microsoft.Testing.Platform/ServerMode/DotnetTest/IPC/Serializers/CommandLineOptionMessagesSerializer.cs b/src/Platform/Microsoft.Testing.Platform/ServerMode/DotnetTest/IPC/Serializers/CommandLineOptionMessagesSerializer.cs index a96255e664..0e75677bd1 100644 --- a/src/Platform/Microsoft.Testing.Platform/ServerMode/DotnetTest/IPC/Serializers/CommandLineOptionMessagesSerializer.cs +++ b/src/Platform/Microsoft.Testing.Platform/ServerMode/DotnetTest/IPC/Serializers/CommandLineOptionMessagesSerializer.cs @@ -45,11 +45,11 @@ public object Deserialize(Stream stream) string? moduleName = null; List? commandLineOptionMessages = null; - ushort fieldCount = ReadShort(stream); + ushort fieldCount = ReadUShort(stream); for (int i = 0; i < fieldCount; i++) { - int fieldId = ReadShort(stream); + int fieldId = ReadUShort(stream); int fieldSize = ReadInt(stream); switch (fieldId) @@ -82,11 +82,11 @@ private static List ReadCommandLineOptionMessagesPaylo string? name = null, description = null; bool? isHidden = null, isBuiltIn = null; - int fieldCount = ReadShort(stream); + int fieldCount = ReadUShort(stream); for (int j = 0; j < fieldCount; j++) { - int fieldId = ReadShort(stream); + int fieldId = ReadUShort(stream); int fieldSize = ReadInt(stream); switch (fieldId) @@ -125,7 +125,7 @@ public void Serialize(object objectToSerialize, Stream stream) var commandLineOptionMessages = (CommandLineOptionMessages)objectToSerialize; - WriteShort(stream, GetFieldCount(commandLineOptionMessages)); + WriteUShort(stream, GetFieldCount(commandLineOptionMessages)); WriteField(stream, CommandLineOptionMessagesFieldsId.ModulePath, commandLineOptionMessages.ModulePath); WriteCommandLineOptionMessagesPayload(stream, commandLineOptionMessages.CommandLineOptionMessageList); @@ -138,7 +138,7 @@ private static void WriteCommandLineOptionMessagesPayload(Stream stream, Command return; } - WriteShort(stream, CommandLineOptionMessagesFieldsId.CommandLineOptionMessageList); + WriteUShort(stream, CommandLineOptionMessagesFieldsId.CommandLineOptionMessageList); // We will reserve an int (4 bytes) // so that we fill the size later, once we write the payload @@ -148,7 +148,7 @@ private static void WriteCommandLineOptionMessagesPayload(Stream stream, Command WriteInt(stream, commandLineOptionMessageList.Length); foreach (CommandLineOptionMessage commandLineOptionMessage in commandLineOptionMessageList) { - WriteShort(stream, GetFieldCount(commandLineOptionMessage)); + WriteUShort(stream, GetFieldCount(commandLineOptionMessage)); WriteField(stream, CommandLineOptionMessageFieldsId.Name, commandLineOptionMessage.Name); WriteField(stream, CommandLineOptionMessageFieldsId.Description, commandLineOptionMessage.Description); diff --git a/src/Platform/Microsoft.Testing.Platform/ServerMode/DotnetTest/IPC/Serializers/DiscoveredTestMessagesSerializer.cs b/src/Platform/Microsoft.Testing.Platform/ServerMode/DotnetTest/IPC/Serializers/DiscoveredTestMessagesSerializer.cs index 0ea49f5d83..dd0d4f39ae 100644 --- a/src/Platform/Microsoft.Testing.Platform/ServerMode/DotnetTest/IPC/Serializers/DiscoveredTestMessagesSerializer.cs +++ b/src/Platform/Microsoft.Testing.Platform/ServerMode/DotnetTest/IPC/Serializers/DiscoveredTestMessagesSerializer.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 Microsoft.Testing.Platform.Extensions.Messages; using Microsoft.Testing.Platform.IPC.Models; namespace Microsoft.Testing.Platform.IPC.Serializers; @@ -30,7 +31,50 @@ namespace Microsoft.Testing.Platform.IPC.Serializers; |---DiscoveredTestMessageList[0].DisplayName Id---| (2 bytes) |---DiscoveredTestMessageList[0].DisplayName Size---| (4 bytes) |---DiscoveredTestMessageList[0].DisplayName Value---| (n bytes) - */ + + |---DiscoveredTestMessageList[0].FilePath Id---| (2 bytes) + |---DiscoveredTestMessageList[0].FilePath Size---| (4 bytes) + |---DiscoveredTestMessageList[0].FilePath Value---| (n bytes) + + |---DiscoveredTestMessageList[0].LineNumber Id---| (2 bytes) + |---DiscoveredTestMessageList[0].LineNumber Size---| (4 bytes) + |---DiscoveredTestMessageList[0].LineNumber Value---| (4 bytes) + + |---DiscoveredTestMessageList[0].Namespace Id---| (2 bytes) + |---DiscoveredTestMessageList[0].Namespace Size---| (4 bytes) + |---DiscoveredTestMessageList[0].Namespace Value---| (n bytes) + + |---DiscoveredTestMessageList[0].TypeName Id---| (2 bytes) + |---DiscoveredTestMessageList[0].TypeName Size---| (4 bytes) + |---DiscoveredTestMessageList[0].TypeName Value---| (n bytes) + + |---DiscoveredTestMessageList[0].MethodName Id---| (2 bytes) + |---DiscoveredTestMessageList[0].MethodName Size---| (4 bytes) + |---DiscoveredTestMessageList[0].MethodName Value---| (n bytes) + + |---DiscoveredTestMessageList[0].Traits Id---| (2 bytes) + |---DiscoveredTestMessageList[0].Traits Size---| (4 bytes) + |---DiscoveredTestMessageList[0].Traits Value---| (n bytes) + |---DiscoveredTestMessageList[0].Traits Length---| (4 bytes) + + |---DiscoveredTestMessageList[0].Trits[0] FieldCount---| 2 bytes + + |---DiscoveredTestMessageList[0].Trits[0].Key Id---| (2 bytes) + |---DiscoveredTestMessageList[0].Trits[0].Key Size---| (4 bytes) + |---DiscoveredTestMessageList[0].Trits[0].Key Value---| (n bytes) + + |---DiscoveredTestMessageList[0].Trits[0].Value Id---| (2 bytes) + |---DiscoveredTestMessageList[0].Trits[0].Value Size---| (4 bytes) + |---DiscoveredTestMessageList[0].Trits[0].Value Value---| (n bytes) + + |---DiscoveredTestMessageList[0].ParameterTypeFullNames Id---| (2 bytes) + |---DiscoveredTestMessageList[0].ParameterTypeFullNames Size---| (4 bytes) + |---DiscoveredTestMessageList[0].ParameterTypeFullNames Value---| (n bytes) + |---DiscoveredTestMessageList[0].ParameterTypeFullNames Length---| (4 bytes) + + |---DiscoveredTestMessageList[0].ParameterTypeFullNames[0].Key Size---| (4 bytes) + |---DiscoveredTestMessageList[0].ParameterTypeFullNames[0].Key Value---| (n bytes) +*/ internal sealed class DiscoveredTestMessagesSerializer : BaseSerializer, INamedPipeSerializer { @@ -40,13 +84,13 @@ public object Deserialize(Stream stream) { string? executionId = null; string? instanceId = null; - List? discoveredTestMessages = null; + DiscoveredTestMessage[]? discoveredTestMessages = []; - ushort fieldCount = ReadShort(stream); + ushort fieldCount = ReadUShort(stream); for (int i = 0; i < fieldCount; i++) { - int fieldId = ReadShort(stream); + int fieldId = ReadUShort(stream); int fieldSize = ReadInt(stream); switch (fieldId) @@ -70,23 +114,30 @@ public object Deserialize(Stream stream) } } - return new DiscoveredTestMessages(executionId, instanceId, discoveredTestMessages is null ? [] : [.. discoveredTestMessages]); + return new DiscoveredTestMessages(executionId, instanceId, discoveredTestMessages); } - private static List ReadDiscoveredTestMessagesPayload(Stream stream) + private static DiscoveredTestMessage[] ReadDiscoveredTestMessagesPayload(Stream stream) { - List discoveredTestMessages = []; - int length = ReadInt(stream); + var discoveredTestMessages = new DiscoveredTestMessage[length]; for (int i = 0; i < length; i++) { - string? uid = null, displayName = null; - - int fieldCount = ReadShort(stream); + string? uid = null; + string? displayName = null; + string? filePath = null; + int? lineNumber = null; + string? @namespace = null; + string? typeName = null; + string? methodName = null; + TestMetadataProperty[] traits = []; + string[] parameterTypeFullNames = []; + + int fieldCount = ReadUShort(stream); for (int j = 0; j < fieldCount; j++) { - int fieldId = ReadShort(stream); + int fieldId = ReadUShort(stream); int fieldSize = ReadInt(stream); switch (fieldId) @@ -99,25 +150,105 @@ private static List ReadDiscoveredTestMessagesPayload(Str displayName = ReadStringValue(stream, fieldSize); break; + case DiscoveredTestMessageFieldsId.FilePath: + filePath = ReadStringValue(stream, fieldSize); + break; + + case DiscoveredTestMessageFieldsId.LineNumber: + lineNumber = ReadInt(stream); + break; + + case DiscoveredTestMessageFieldsId.Namespace: + @namespace = ReadStringValue(stream, fieldSize); + break; + + case DiscoveredTestMessageFieldsId.TypeName: + typeName = ReadStringValue(stream, fieldSize); + break; + + case DiscoveredTestMessageFieldsId.MethodName: + methodName = ReadStringValue(stream, fieldSize); + break; + + case DiscoveredTestMessageFieldsId.Traits: + traits = ReadTraitsPayload(stream); + break; + + case DiscoveredTestMessageFieldsId.ParameterTypeFullNames: + parameterTypeFullNames = ReadParameterTypeFullNamesPayload(stream); + break; + default: SetPosition(stream, stream.Position + fieldSize); break; } } - discoveredTestMessages.Add(new DiscoveredTestMessage(uid, displayName)); + discoveredTestMessages[i] = new DiscoveredTestMessage(uid, displayName, filePath, lineNumber, @namespace, typeName, methodName, parameterTypeFullNames, traits); } return discoveredTestMessages; } + private static string[] ReadParameterTypeFullNamesPayload(Stream stream) + { + int length = ReadInt(stream); + string[] parameterTypeFullNames = new string[length]; + + for (int i = 0; i < length; i++) + { + parameterTypeFullNames[i] = ReadString(stream); + } + + return parameterTypeFullNames; + } + + private static TestMetadataProperty[] ReadTraitsPayload(Stream stream) + { + int length = ReadInt(stream); + var traits = new TestMetadataProperty[length]; + for (int i = 0; i < length; i++) + { + string? key = null; + string? value = null; + int fieldCount = ReadUShort(stream); + + for (int j = 0; j < fieldCount; j++) + { + int fieldId = ReadUShort(stream); + int fieldSize = ReadInt(stream); + + switch (fieldId) + { + case TraitMessageFieldsId.Key: + key = ReadStringValue(stream, fieldSize); + break; + + case TraitMessageFieldsId.Value: + value = ReadStringValue(stream, fieldSize); + break; + + default: + SetPosition(stream, stream.Position + fieldSize); + break; + } + } + + Guard.NotNull(key); + Guard.NotNull(value); + traits[i] = new TestMetadataProperty(key, value); + } + + return traits; + } + public void Serialize(object objectToSerialize, Stream stream) { RoslynDebug.Assert(stream.CanSeek, "We expect a seekable stream."); var discoveredTestMessages = (DiscoveredTestMessages)objectToSerialize; - WriteShort(stream, GetFieldCount(discoveredTestMessages)); + WriteUShort(stream, GetFieldCount(discoveredTestMessages)); WriteField(stream, DiscoveredTestMessagesFieldsId.ExecutionId, discoveredTestMessages.ExecutionId); WriteField(stream, DiscoveredTestMessagesFieldsId.InstanceId, discoveredTestMessages.InstanceId); @@ -131,7 +262,7 @@ private static void WriteDiscoveredTestMessagesPayload(Stream stream, Discovered return; } - WriteShort(stream, DiscoveredTestMessagesFieldsId.DiscoveredTestMessageList); + WriteUShort(stream, DiscoveredTestMessagesFieldsId.DiscoveredTestMessageList); // We will reserve an int (4 bytes) // so that we fill the size later, once we write the payload @@ -141,10 +272,70 @@ private static void WriteDiscoveredTestMessagesPayload(Stream stream, Discovered WriteInt(stream, discoveredTestMessageList.Length); foreach (DiscoveredTestMessage discoveredTestMessage in discoveredTestMessageList) { - WriteShort(stream, GetFieldCount(discoveredTestMessage)); + WriteUShort(stream, GetFieldCount(discoveredTestMessage)); WriteField(stream, DiscoveredTestMessageFieldsId.Uid, discoveredTestMessage.Uid); WriteField(stream, DiscoveredTestMessageFieldsId.DisplayName, discoveredTestMessage.DisplayName); + WriteField(stream, DiscoveredTestMessageFieldsId.FilePath, discoveredTestMessage.FilePath); + WriteField(stream, DiscoveredTestMessageFieldsId.LineNumber, discoveredTestMessage.LineNumber); + WriteField(stream, DiscoveredTestMessageFieldsId.Namespace, discoveredTestMessage.Namespace); + WriteField(stream, DiscoveredTestMessageFieldsId.TypeName, discoveredTestMessage.TypeName); + WriteField(stream, DiscoveredTestMessageFieldsId.MethodName, discoveredTestMessage.MethodName); + WriteParameterTypeFullNamesPayload(stream, discoveredTestMessage.ParameterTypeFullNames); + WriteTraitsPayload(stream, discoveredTestMessage.Traits); + } + + // 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 void WriteTraitsPayload(Stream stream, TestMetadataProperty[]? traits) + { + if (traits is null || traits.Length == 0) + { + return; + } + + WriteUShort(stream, DiscoveredTestMessageFieldsId.Traits); + + // 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, traits.Length); + foreach (TestMetadataProperty trait in traits) + { + WriteUShort(stream, GetFieldCount(trait)); + + WriteField(stream, TraitMessageFieldsId.Key, trait.Key); + WriteField(stream, TraitMessageFieldsId.Value, trait.Value); + } + + // 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 void WriteParameterTypeFullNamesPayload(Stream stream, string[]? parameterTypeFullNames) + { + if (parameterTypeFullNames is null || parameterTypeFullNames.Length == 0) + { + return; + } + + WriteUShort(stream, DiscoveredTestMessageFieldsId.ParameterTypeFullNames); + + // 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, parameterTypeFullNames.Length); + foreach (string parameterTypeFullName in parameterTypeFullNames) + { + WriteString(stream, parameterTypeFullName); } // NOTE: We are able to seek only if we are using a MemoryStream @@ -159,5 +350,16 @@ private static ushort GetFieldCount(DiscoveredTestMessages discoveredTestMessage private static ushort GetFieldCount(DiscoveredTestMessage discoveredTestMessage) => (ushort)((discoveredTestMessage.Uid is null ? 0 : 1) + - (discoveredTestMessage.DisplayName is null ? 0 : 1)); + (discoveredTestMessage.DisplayName is null ? 0 : 1) + + (discoveredTestMessage.FilePath is null ? 0 : 1) + + (discoveredTestMessage.LineNumber is null ? 0 : 1) + + (discoveredTestMessage.Namespace is null ? 0 : 1) + + (discoveredTestMessage.TypeName is null ? 0 : 1) + + (discoveredTestMessage.MethodName is null ? 0 : 1) + + (IsNullOrEmpty(discoveredTestMessage.ParameterTypeFullNames) ? 0 : 1) + + (IsNullOrEmpty(discoveredTestMessage.Traits) ? 0 : 1)); + + private static ushort GetFieldCount(TestMetadataProperty trait) => + (ushort)((trait.Key is null ? 0 : 1) + + (trait.Value is null ? 0 : 1)); } diff --git a/src/Platform/Microsoft.Testing.Platform/ServerMode/DotnetTest/IPC/Serializers/FileArtifactMessagesSerializer.cs b/src/Platform/Microsoft.Testing.Platform/ServerMode/DotnetTest/IPC/Serializers/FileArtifactMessagesSerializer.cs index 203b295ef8..614fe806bc 100644 --- a/src/Platform/Microsoft.Testing.Platform/ServerMode/DotnetTest/IPC/Serializers/FileArtifactMessagesSerializer.cs +++ b/src/Platform/Microsoft.Testing.Platform/ServerMode/DotnetTest/IPC/Serializers/FileArtifactMessagesSerializer.cs @@ -58,11 +58,11 @@ public object Deserialize(Stream stream) string? instanceId = null; List? fileArtifactMessages = null; - ushort fieldCount = ReadShort(stream); + ushort fieldCount = ReadUShort(stream); for (int i = 0; i < fieldCount; i++) { - int fieldId = ReadShort(stream); + int fieldId = ReadUShort(stream); int fieldSize = ReadInt(stream); switch (fieldId) @@ -98,11 +98,11 @@ private static List ReadFileArtifactMessagesPayload(Stream { string? fullPath = null, displayName = null, description = null, testUid = null, testDisplayName = null, sessionUid = null; - int fieldCount = ReadShort(stream); + int fieldCount = ReadUShort(stream); for (int j = 0; j < fieldCount; j++) { - int fieldId = ReadShort(stream); + int fieldId = ReadUShort(stream); int fieldSize = ReadInt(stream); switch (fieldId) @@ -149,7 +149,7 @@ public void Serialize(object objectToSerialize, Stream stream) var fileArtifactMessages = (FileArtifactMessages)objectToSerialize; - WriteShort(stream, GetFieldCount(fileArtifactMessages)); + WriteUShort(stream, GetFieldCount(fileArtifactMessages)); WriteField(stream, FileArtifactMessagesFieldsId.ExecutionId, fileArtifactMessages.ExecutionId); WriteField(stream, FileArtifactMessagesFieldsId.InstanceId, fileArtifactMessages.InstanceId); @@ -163,7 +163,7 @@ private static void WriteFileArtifactMessagesPayload(Stream stream, FileArtifact return; } - WriteShort(stream, FileArtifactMessagesFieldsId.FileArtifactMessageList); + WriteUShort(stream, FileArtifactMessagesFieldsId.FileArtifactMessageList); // We will reserve an int (4 bytes) // so that we fill the size later, once we write the payload @@ -173,7 +173,7 @@ private static void WriteFileArtifactMessagesPayload(Stream stream, FileArtifact WriteInt(stream, fileArtifactMessageList.Length); foreach (FileArtifactMessage fileArtifactMessage in fileArtifactMessageList) { - WriteShort(stream, GetFieldCount(fileArtifactMessage)); + WriteUShort(stream, GetFieldCount(fileArtifactMessage)); WriteField(stream, FileArtifactMessageFieldsId.FullPath, fileArtifactMessage.FullPath); WriteField(stream, FileArtifactMessageFieldsId.DisplayName, fileArtifactMessage.DisplayName); diff --git a/src/Platform/Microsoft.Testing.Platform/ServerMode/DotnetTest/IPC/Serializers/HandshakeMessageSerializer.cs b/src/Platform/Microsoft.Testing.Platform/ServerMode/DotnetTest/IPC/Serializers/HandshakeMessageSerializer.cs index 9940477af9..554ae3b12d 100644 --- a/src/Platform/Microsoft.Testing.Platform/ServerMode/DotnetTest/IPC/Serializers/HandshakeMessageSerializer.cs +++ b/src/Platform/Microsoft.Testing.Platform/ServerMode/DotnetTest/IPC/Serializers/HandshakeMessageSerializer.cs @@ -11,9 +11,9 @@ internal sealed class HandshakeMessageSerializer : BaseSerializer, INamedPipeSer public object Deserialize(Stream stream) { - Dictionary properties = new(); + Dictionary properties = []; - ushort fieldCount = ReadShort(stream); + ushort fieldCount = ReadUShort(stream); for (int i = 0; i < fieldCount; i++) { @@ -29,12 +29,15 @@ public void Serialize(object objectToSerialize, Stream stream) var handshakeMessage = (HandshakeMessage)objectToSerialize; - if (handshakeMessage.Properties is null || handshakeMessage.Properties.Count == 0) + // Deserializer always expected fieldCount to be present. + // We must write the count even if Properties is null or empty. + WriteUShort(stream, (ushort)(handshakeMessage.Properties?.Count ?? 0)); + + if (handshakeMessage.Properties is null) { return; } - WriteShort(stream, (ushort)handshakeMessage.Properties.Count); foreach ((byte key, string value) in handshakeMessage.Properties) { WriteField(stream, key); diff --git a/src/Platform/Microsoft.Testing.Platform/ServerMode/DotnetTest/IPC/Serializers/TestResultMessagesSerializer.cs b/src/Platform/Microsoft.Testing.Platform/ServerMode/DotnetTest/IPC/Serializers/TestResultMessagesSerializer.cs index 4472ddbaea..6535d43e12 100644 --- a/src/Platform/Microsoft.Testing.Platform/ServerMode/DotnetTest/IPC/Serializers/TestResultMessagesSerializer.cs +++ b/src/Platform/Microsoft.Testing.Platform/ServerMode/DotnetTest/IPC/Serializers/TestResultMessagesSerializer.cs @@ -114,11 +114,11 @@ public object Deserialize(Stream stream) List? successfulTestResultMessages = null; List? failedTestResultMessages = null; - ushort fieldCount = ReadShort(stream); + ushort fieldCount = ReadUShort(stream); for (int i = 0; i < fieldCount; i++) { - int fieldId = ReadShort(stream); + int fieldId = ReadUShort(stream); int fieldSize = ReadInt(stream); switch (fieldId) @@ -164,11 +164,11 @@ private static List ReadSuccessfulTestMessagesPaylo byte? state = null; long? duration = null; - int fieldCount = ReadShort(stream); + int fieldCount = ReadUShort(stream); for (int j = 0; j < fieldCount; j++) { - int fieldId = ReadShort(stream); + int fieldId = ReadUShort(stream); int fieldSize = ReadInt(stream); switch (fieldId) @@ -229,11 +229,11 @@ private static List ReadFailedTestMessagesPayload(Strea byte? state = null; long? duration = null; - int fieldCount = ReadShort(stream); + int fieldCount = ReadUShort(stream); for (int j = 0; j < fieldCount; j++) { - int fieldId = ReadShort(stream); + int fieldId = ReadUShort(stream); int fieldSize = ReadInt(stream); switch (fieldId) @@ -293,7 +293,7 @@ private static ExceptionMessage[] ReadExceptionMessagesPayload(Stream stream) int length = ReadInt(stream); for (int i = 0; i < length; i++) { - int fieldCount = ReadShort(stream); + int fieldCount = ReadUShort(stream); string? errorMessage = null; string? errorType = null; @@ -301,7 +301,7 @@ private static ExceptionMessage[] ReadExceptionMessagesPayload(Stream stream) for (int j = 0; j < fieldCount; j++) { - int fieldId = ReadShort(stream); + int fieldId = ReadUShort(stream); int fieldSize = ReadInt(stream); switch (fieldId) @@ -323,7 +323,7 @@ private static ExceptionMessage[] ReadExceptionMessagesPayload(Stream stream) exceptionMessages.Add(new ExceptionMessage(errorMessage, errorType, stackTrace)); } - return exceptionMessages.ToArray(); + return [.. exceptionMessages]; } public void Serialize(object objectToSerialize, Stream stream) @@ -332,7 +332,7 @@ public void Serialize(object objectToSerialize, Stream stream) var testResultMessages = (TestResultMessages)objectToSerialize; - WriteShort(stream, GetFieldCount(testResultMessages)); + WriteUShort(stream, GetFieldCount(testResultMessages)); WriteField(stream, TestResultMessagesFieldsId.ExecutionId, testResultMessages.ExecutionId); WriteField(stream, TestResultMessagesFieldsId.InstanceId, testResultMessages.InstanceId); @@ -347,7 +347,7 @@ private static void WriteSuccessfulTestMessagesPayload(Stream stream, Successful return; } - WriteShort(stream, TestResultMessagesFieldsId.SuccessfulTestMessageList); + WriteUShort(stream, TestResultMessagesFieldsId.SuccessfulTestMessageList); // We will reserve an int (4 bytes) // so that we fill the size later, once we write the payload @@ -357,7 +357,7 @@ private static void WriteSuccessfulTestMessagesPayload(Stream stream, Successful WriteInt(stream, successfulTestResultMessages.Length); foreach (SuccessfulTestResultMessage successfulTestResultMessage in successfulTestResultMessages) { - WriteShort(stream, GetFieldCount(successfulTestResultMessage)); + WriteUShort(stream, GetFieldCount(successfulTestResultMessage)); WriteField(stream, SuccessfulTestResultMessageFieldsId.Uid, successfulTestResultMessage.Uid); WriteField(stream, SuccessfulTestResultMessageFieldsId.DisplayName, successfulTestResultMessage.DisplayName); @@ -381,7 +381,7 @@ private static void WriteFailedTestMessagesPayload(Stream stream, FailedTestResu return; } - WriteShort(stream, TestResultMessagesFieldsId.FailedTestMessageList); + WriteUShort(stream, TestResultMessagesFieldsId.FailedTestMessageList); // We will reserve an int (4 bytes) // so that we fill the size later, once we write the payload @@ -391,7 +391,7 @@ private static void WriteFailedTestMessagesPayload(Stream stream, FailedTestResu WriteInt(stream, failedTestResultMessages.Length); foreach (FailedTestResultMessage failedTestResultMessage in failedTestResultMessages) { - WriteShort(stream, GetFieldCount(failedTestResultMessage)); + WriteUShort(stream, GetFieldCount(failedTestResultMessage)); WriteField(stream, FailedTestResultMessageFieldsId.Uid, failedTestResultMessage.Uid); WriteField(stream, FailedTestResultMessageFieldsId.DisplayName, failedTestResultMessage.DisplayName); @@ -416,7 +416,7 @@ private static void WriteExceptionMessagesPayload(Stream stream, ExceptionMessag return; } - WriteShort(stream, FailedTestResultMessageFieldsId.ExceptionMessageList); + WriteUShort(stream, FailedTestResultMessageFieldsId.ExceptionMessageList); // We will reserve an int (4 bytes) // so that we fill the size later, once we write the payload @@ -426,7 +426,7 @@ private static void WriteExceptionMessagesPayload(Stream stream, ExceptionMessag WriteInt(stream, exceptionMessages.Length); foreach (ExceptionMessage exceptionMessage in exceptionMessages) { - WriteShort(stream, GetFieldCount(exceptionMessage)); + WriteUShort(stream, GetFieldCount(exceptionMessage)); WriteField(stream, ExceptionMessageFieldsId.ErrorMessage, exceptionMessage.ErrorMessage); WriteField(stream, ExceptionMessageFieldsId.ErrorType, exceptionMessage.ErrorType); diff --git a/src/Platform/Microsoft.Testing.Platform/ServerMode/DotnetTest/IPC/Serializers/TestSessionEventSerializer.cs b/src/Platform/Microsoft.Testing.Platform/ServerMode/DotnetTest/IPC/Serializers/TestSessionEventSerializer.cs index 2c5afdefa1..0862b3f69f 100644 --- a/src/Platform/Microsoft.Testing.Platform/ServerMode/DotnetTest/IPC/Serializers/TestSessionEventSerializer.cs +++ b/src/Platform/Microsoft.Testing.Platform/ServerMode/DotnetTest/IPC/Serializers/TestSessionEventSerializer.cs @@ -31,11 +31,11 @@ public object Deserialize(Stream stream) string? sessionUid = null; string? executionId = null; - ushort fieldCount = ReadShort(stream); + ushort fieldCount = ReadUShort(stream); for (int i = 0; i < fieldCount; i++) { - ushort fieldId = ReadShort(stream); + ushort fieldId = ReadUShort(stream); int fieldSize = ReadInt(stream); switch (fieldId) @@ -68,7 +68,7 @@ public void Serialize(object objectToSerialize, Stream stream) var testSessionEvent = (TestSessionEvent)objectToSerialize; - WriteShort(stream, GetFieldCount(testSessionEvent)); + WriteUShort(stream, GetFieldCount(testSessionEvent)); WriteField(stream, TestSessionEventFieldsId.SessionType, testSessionEvent.SessionType); WriteField(stream, TestSessionEventFieldsId.SessionUid, testSessionEvent.SessionUid); diff --git a/src/Platform/Microsoft.Testing.Platform/ServerMode/JsonRpc/FormatterUtilities.cs b/src/Platform/Microsoft.Testing.Platform/ServerMode/JsonRpc/FormatterUtilities.cs index 0e540ef32e..f5a40ec03f 100644 --- a/src/Platform/Microsoft.Testing.Platform/ServerMode/JsonRpc/FormatterUtilities.cs +++ b/src/Platform/Microsoft.Testing.Platform/ServerMode/JsonRpc/FormatterUtilities.cs @@ -34,13 +34,13 @@ internal sealed class MessageFormatter : IMessageFormatter public MessageFormatter() { - Dictionary serializers = new(); - Dictionary deserializers = new(); + Dictionary serializers = []; + Dictionary deserializers = []; foreach (Type serializableType in SerializerUtilities.SerializerTypes) { serializers[serializableType] = new JsonObjectSerializer( - o => SerializerUtilities.Serialize(serializableType, o).Select(kvp => (kvp.Key, kvp.Value)).ToArray()); + o => [.. SerializerUtilities.Serialize(serializableType, o).Select(kvp => (kvp.Key, kvp.Value))]); } foreach (Type deserializableType in SerializerUtilities.DeserializerTypes) diff --git a/src/Platform/Microsoft.Testing.Platform/ServerMode/JsonRpc/IServerModeManager.cs b/src/Platform/Microsoft.Testing.Platform/ServerMode/JsonRpc/IServerModeManager.cs deleted file mode 100644 index dde41a1521..0000000000 --- a/src/Platform/Microsoft.Testing.Platform/ServerMode/JsonRpc/IServerModeManager.cs +++ /dev/null @@ -1,6 +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.ServerMode; - -internal interface IServerModeManager; diff --git a/src/Platform/Microsoft.Testing.Platform/ServerMode/JsonRpc/Json/Json.cs b/src/Platform/Microsoft.Testing.Platform/ServerMode/JsonRpc/Json/Json.cs index d7b1b73607..e2597d52b6 100644 --- a/src/Platform/Microsoft.Testing.Platform/ServerMode/JsonRpc/Json/Json.cs +++ b/src/Platform/Microsoft.Testing.Platform/ServerMode/JsonRpc/Json/Json.cs @@ -19,28 +19,27 @@ public Json(Dictionary? serializers = null, Dictionary(request => new[] - { - (JsonRpcStrings.JsonRpc, "2.0"), + _serializers[typeof(RequestMessage)] = new JsonObjectSerializer(request => + [ + (JsonRpcStrings.JsonRpc, "2.0"), (JsonRpcStrings.Id, request.Id), (JsonRpcStrings.Method, request.Method), - (JsonRpcStrings.Params, request.Params), - }); + (JsonRpcStrings.Params, request.Params) + ]); - _serializers[typeof(ResponseMessage)] = new JsonObjectSerializer(response => new[] - { - (JsonRpcStrings.JsonRpc, "2.0"), + _serializers[typeof(ResponseMessage)] = new JsonObjectSerializer(response => + [ + (JsonRpcStrings.JsonRpc, "2.0"), (JsonRpcStrings.Id, response.Id), - (JsonRpcStrings.Result, response.Result), - }); + (JsonRpcStrings.Result, response.Result) + ]); _serializers[typeof(NotificationMessage)] = new JsonObjectSerializer(notification => - new[] - { - (JsonRpcStrings.JsonRpc, "2.0"), + [ + (JsonRpcStrings.JsonRpc, "2.0"), (JsonRpcStrings.Method, notification.Method), - (JsonRpcStrings.Params, notification.Params), - }); + (JsonRpcStrings.Params, notification.Params) + ]); _serializers[typeof(ErrorMessage)] = new JsonObjectSerializer(error => { @@ -51,76 +50,69 @@ public Json(Dictionary? serializers = null, Dictionary(response => - new (string, object?)[] - { - (JsonRpcStrings.ProcessId, response.ProcessId), + [ + (JsonRpcStrings.ProcessId, response.ProcessId), (JsonRpcStrings.ServerInfo, response.ServerInfo), - (JsonRpcStrings.Capabilities, response.Capabilities), - }); + (JsonRpcStrings.Capabilities, response.Capabilities) + ]); - _serializers[typeof(ServerInfo)] = new JsonObjectSerializer(info => new (string, object?)[] - { - (JsonRpcStrings.Name, info.Name), - (JsonRpcStrings.Version, info.Version), - }); + _serializers[typeof(ServerInfo)] = new JsonObjectSerializer(info => + [ + (JsonRpcStrings.Name, info.Name), + (JsonRpcStrings.Version, info.Version) + ]); _serializers[typeof(ServerCapabilities)] = new JsonObjectSerializer(capabilities => - new (string, object?)[] - { - (JsonRpcStrings.Testing, capabilities.TestingCapabilities), - }); + [ + (JsonRpcStrings.Testing, capabilities.TestingCapabilities) + ]); _serializers[typeof(ServerTestingCapabilities)] = new JsonObjectSerializer(capabilities => - new (string, object?)[] - { - (JsonRpcStrings.SupportsDiscovery, capabilities.SupportsDiscovery), + [ + (JsonRpcStrings.SupportsDiscovery, capabilities.SupportsDiscovery), (JsonRpcStrings.MultiRequestSupport, capabilities.MultiRequestSupport), (JsonRpcStrings.VSTestProviderSupport, capabilities.VSTestProviderSupport), (JsonRpcStrings.AttachmentsSupport, capabilities.SupportsAttachments), - (JsonRpcStrings.MultiConnectionProvider, capabilities.MultiConnectionProvider), - }); + (JsonRpcStrings.MultiConnectionProvider, capabilities.MultiConnectionProvider) + ]); _serializers[typeof(Artifact)] = new JsonObjectSerializer(artifact => - new (string, object?)[] - { - (JsonRpcStrings.Uri, artifact.Uri), + [ + (JsonRpcStrings.Uri, artifact.Uri), (JsonRpcStrings.Producer, artifact.Producer), (JsonRpcStrings.Type, artifact.Type), (JsonRpcStrings.DisplayName, artifact.DisplayName), - (JsonRpcStrings.Description, artifact.Description), - }); + (JsonRpcStrings.Description, artifact.Description) + ]); _serializers[typeof(DiscoverResponseArgs)] = new JsonObjectSerializer(response => []); _serializers[typeof(RunResponseArgs)] = new JsonObjectSerializer(response => - new (string, object?)[] - { - (JsonRpcStrings.Attachments, response.Artifacts), - }); + [ + (JsonRpcStrings.Attachments, response.Artifacts) + ]); _serializers[typeof(TestNodeUpdateMessage)] = new JsonObjectSerializer(message => - new (string, object?)[] - { - (JsonRpcStrings.Node, message.TestNode), - (JsonRpcStrings.Parent, message.ParentTestNodeUid?.Value), - }); + [ + (JsonRpcStrings.Node, message.TestNode), + (JsonRpcStrings.Parent, message.ParentTestNodeUid?.Value) + ]); _serializers[typeof(TestNodeStateChangedEventArgs)] = new JsonObjectSerializer(message => - new (string, object?)[] - { - (JsonRpcStrings.RunId, message.RunId), - (JsonRpcStrings.Changes, message.Changes), - }); + [ + (JsonRpcStrings.RunId, message.RunId), + (JsonRpcStrings.Changes, message.Changes) + ]); _serializers[typeof(TestNode)] = new JsonObjectSerializer(message => { @@ -162,7 +154,7 @@ public Json(Dictionary? serializers = null, Dictionary 0 - ? $"{testMethodIdentifierProperty.MethodName}({string.Join(",", testMethodIdentifierProperty.ParameterTypeFullNames)})" + ? $"{testMethodIdentifierProperty.MethodName}({string.Join(',', testMethodIdentifierProperty.ParameterTypeFullNames)})" : testMethodIdentifierProperty.MethodName)); properties.Add(("location.method-arity", testMethodIdentifierProperty.MethodArity)); @@ -278,8 +270,6 @@ public Json(Dictionary? serializers = null, Dictionary? serializers = null, Dictionary(message => - new (string, object?)[] - { - (JsonRpcStrings.Level, message.LogMessage.Level.ToString()), - (JsonRpcStrings.Message, message.LogMessage.Message), - }); + [ + (JsonRpcStrings.Level, message.LogMessage.Level.ToString()), + (JsonRpcStrings.Message, message.LogMessage.Message) + ]); _serializers[typeof(CancelRequestArgs)] = new JsonObjectSerializer(request => - new (string, object?)[] - { - (JsonRpcStrings.Id, request.CancelRequestId), - }); + [ + (JsonRpcStrings.Id, request.CancelRequestId) + ]); _serializers[typeof(TelemetryEventArgs)] = new JsonObjectSerializer(ev => - new (string, object?)[] - { - (JsonRpcStrings.EventName, ev.EventName), - (JsonRpcStrings.Metrics, ev.Metrics), - }); + [ + (JsonRpcStrings.EventName, ev.EventName), + (JsonRpcStrings.Metrics, ev.Metrics) + ]); _serializers[typeof(ProcessInfoArgs)] = new JsonObjectSerializer(info => - new (string, object?)[] - { - (JsonRpcStrings.Program, info.Program), + [ + (JsonRpcStrings.Program, info.Program), (JsonRpcStrings.Args, info.Args), (JsonRpcStrings.WorkingDirectory, info.WorkingDirectory), - (JsonRpcStrings.EnvironmentVariables, info.EnvironmentVariables), - }); + (JsonRpcStrings.EnvironmentVariables, info.EnvironmentVariables) + ]); _serializers[typeof(AttachDebuggerInfoArgs)] = new JsonObjectSerializer(info => - new (string, object?)[] - { - (JsonRpcStrings.ProcessId, info.ProcessId), - }); + [ + (JsonRpcStrings.ProcessId, info.ProcessId) + ]); _serializers[typeof(TestsAttachments)] = new JsonObjectSerializer(info => - new (string, object?)[] - { - (JsonRpcStrings.Attachments, info.Attachments), - }); + [ + (JsonRpcStrings.Attachments, info.Attachments) + ]); _serializers[typeof(RunTestAttachment)] = new JsonObjectSerializer(info => - new (string, object?)[] - { - (JsonRpcStrings.Uri, info.Uri), + [ + (JsonRpcStrings.Uri, info.Uri), (JsonRpcStrings.Producer, info.Producer), (JsonRpcStrings.Type, info.Type), (JsonRpcStrings.DisplayName, info.DisplayName), - (JsonRpcStrings.Description, info.Description), - }); + (JsonRpcStrings.Description, info.Description) + ]); // Serializers _serializers[typeof(string)] = new JsonValueSerializer((w, v) => w.WriteStringValue(v)); @@ -368,7 +351,7 @@ public Json(Dictionary? serializers = null, Dictionary((w, v) => w.WriteStringValue(v.ToString())); // Remove for now _serializers[typeof((string, object?)[])] = new JsonObjectSerializer<(string, object?)[]>(n => n); - _serializers[typeof(Dictionary)] = new JsonObjectSerializer>(d => d.Select(kvp => (kvp.Key, (object?)kvp.Value)).ToArray()); + _serializers[typeof(Dictionary)] = new JsonObjectSerializer>(d => [.. d.Select(kvp => (kvp.Key, (object?)kvp.Value))]); // Deserializers _deserializers[typeof(string)] = new JsonElementDeserializer((json, jsonDocument) => jsonDocument.GetString()!); @@ -379,7 +362,7 @@ public Json(Dictionary? serializers = null, Dictionary)] = new JsonElementDeserializer>((json, jsonDocument) => { - Dictionary items = new(); + Dictionary items = []; foreach (JsonProperty kvp in jsonDocument.EnumerateObject()) { switch (kvp.Value.ValueKind) @@ -618,14 +601,12 @@ public async Task SerializeAsync(object obj) try { stream.Position = 0; +#pragma warning disable CA2007 // Consider calling ConfigureAwait on the awaited task await using Utf8JsonWriter writer = new(stream); - await SerializeAsync(obj, writer); - await writer.FlushAsync(); -#if NETCOREAPP +#pragma warning restore CA2007 // Consider calling ConfigureAwait on the awaited task + await SerializeAsync(obj, writer).ConfigureAwait(false); + await writer.FlushAsync().ConfigureAwait(false); return Encoding.UTF8.GetString(stream.GetBuffer().AsMemory().Span[..(int)stream.Position]); -#else - return Encoding.UTF8.GetString(stream.ToArray()); -#endif } finally { @@ -639,13 +620,6 @@ public T Deserialize(ReadOnlyMemory utf8Json) return Bind(document.RootElement, null); } - internal T Bind(IEnumerable properties) - => !_deserializers.TryGetValue(typeof(T), out JsonDeserializer? deserializer) - ? throw new InvalidOperationException($"Cannot find deserializer for {typeof(T)}.") - : deserializer is not JsonPropertyCollectionDeserializer propertyBagDeserializer - ? throw new InvalidOperationException("we need property bag deserializer") - : propertyBagDeserializer.CreateObject(this, properties); - internal T Bind(JsonElement element, string? property = null) { if (property is not null) @@ -683,7 +657,7 @@ internal bool TryArrayBind(JsonElement element, out T[]? value, string? prope return false; } - value = element.EnumerateArray().Select(Deserialize).ToArray(); + value = [.. element.EnumerateArray().Select(Deserialize)]; return true; } @@ -733,12 +707,10 @@ private async Task SerializeAsync(object? obj, Utf8JsonWriter writer) (string Key, object? Value)[]? properties = objectConverter.Properties(obj); if (properties is not null) { - int count = 1; foreach ((string property, object? value) in properties) { writer.WritePropertyName(property); - await SerializeAsync(value, writer); - count++; + await SerializeAsync(value, writer).ConfigureAwait(false); } } @@ -765,7 +737,7 @@ private async Task SerializeAsync(object? obj, Utf8JsonWriter writer) } else { - await SerializeAsync(o, writer); + await SerializeAsync(o, writer).ConfigureAwait(false); } } diff --git a/src/Platform/Microsoft.Testing.Platform/ServerMode/JsonRpc/Json/JsonCollectionDeserializer.cs b/src/Platform/Microsoft.Testing.Platform/ServerMode/JsonRpc/Json/JsonCollectionDeserializer.cs index b22ae843f5..53570dcbcf 100644 --- a/src/Platform/Microsoft.Testing.Platform/ServerMode/JsonRpc/Json/JsonCollectionDeserializer.cs +++ b/src/Platform/Microsoft.Testing.Platform/ServerMode/JsonRpc/Json/JsonCollectionDeserializer.cs @@ -9,32 +9,3 @@ internal abstract class JsonCollectionDeserializer : JsonDeserializ { internal abstract TCollection CreateObject(Json json, JsonElement element); } - -internal sealed class JsonCollectionDeserializer(Func createCollection, Action addItem) : JsonCollectionDeserializer - where TCollection : ICollection -{ - private readonly Func _createCollection = createCollection; - private readonly Action _addItem = addItem; - - public TCollection CreateCollection(JsonElement jsonElement) - => _createCollection(jsonElement); - - public void AddItem(TCollection collection, TItem item) - => _addItem(collection, item); - - internal override TCollection CreateObject(Json json, JsonElement element) - { - if (element.ValueKind == JsonValueKind.Null) - { - return default!; - } - - TCollection collection = CreateCollection(element); - foreach (JsonElement item in element.EnumerateArray()) - { - AddItem(collection, json.Bind(item)); - } - - return collection; - } -} diff --git a/src/Platform/Microsoft.Testing.Platform/ServerMode/JsonRpc/Json/JsonPropertyBagDeserializer.cs b/src/Platform/Microsoft.Testing.Platform/ServerMode/JsonRpc/Json/JsonPropertyBagDeserializer.cs deleted file mode 100644 index 08e5e8c4c9..0000000000 --- a/src/Platform/Microsoft.Testing.Platform/ServerMode/JsonRpc/Json/JsonPropertyBagDeserializer.cs +++ /dev/null @@ -1,14 +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.Text.Json; - -namespace Microsoft.Testing.Platform.ServerMode.Json; - -internal sealed class JsonPropertyCollectionDeserializer(Func, T> creator) : JsonDeserializer -{ - private readonly Func, T> _creator = creator; - - internal T CreateObject(Json json, IEnumerable properties) - => _creator(json, properties); -} diff --git a/src/Platform/Microsoft.Testing.Platform/ServerMode/JsonRpc/JsonRpcTcpServerToSingleClient.cs b/src/Platform/Microsoft.Testing.Platform/ServerMode/JsonRpc/JsonRpcTcpServerToSingleClient.cs deleted file mode 100644 index 93865858e6..0000000000 --- a/src/Platform/Microsoft.Testing.Platform/ServerMode/JsonRpc/JsonRpcTcpServerToSingleClient.cs +++ /dev/null @@ -1,20 +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.Helpers; -using Microsoft.Testing.Platform.Resources; - -namespace Microsoft.Testing.Platform.ServerMode; - -internal sealed class JsonRpcTcpServerToSingleClient(string clientHostName, int clientPort) : ICommunicationProtocol -{ - public string ClientHostName { get; } = clientHostName; - - public int ClientPort { get; } = clientPort; - - public string Name => nameof(JsonRpcTcpServerToSingleClient); - - public string Version => AppVersion.DefaultSemVer; - - public string Description => PlatformResources.JsonRpcTcpServerToSingleClientDescription; -} diff --git a/src/Platform/Microsoft.Testing.Platform/ServerMode/JsonRpc/MessageHandlerFactory.cs b/src/Platform/Microsoft.Testing.Platform/ServerMode/JsonRpc/MessageHandlerFactory.cs index 6c578c2197..3deba13219 100644 --- a/src/Platform/Microsoft.Testing.Platform/ServerMode/JsonRpc/MessageHandlerFactory.cs +++ b/src/Platform/Microsoft.Testing.Platform/ServerMode/JsonRpc/MessageHandlerFactory.cs @@ -15,7 +15,7 @@ internal sealed partial class ServerModeManager { internal sealed class MessageHandlerFactory : IMessageHandlerFactory, IOutputDeviceDataProducer { - private readonly string? _host; + private readonly string _host; private readonly int _port; private readonly IOutputDevice _outputDevice; @@ -39,52 +39,21 @@ public MessageHandlerFactory( public string Description => nameof(MessageHandlerFactory); - public Task CreateMessageHandlerAsync(CancellationToken cancellationToken) - => _host is not null - ? ConnectToTestPlatformClientAsync(_host, _port, cancellationToken) - : StartTestPlatformServerAsync(port: _port, cancellationToken); - #pragma warning disable CA1416 // Validate platform compatibility - private async Task ConnectToTestPlatformClientAsync(string clientHost, int clientPort, CancellationToken cancellationToken) + public async Task CreateMessageHandlerAsync(CancellationToken cancellationToken) { - await _outputDevice.DisplayAsync(this, new TextOutputDeviceData(string.Format(CultureInfo.InvariantCulture, PlatformResources.ConnectingToClientHost, clientHost, clientPort))); + await _outputDevice.DisplayAsync(this, new TextOutputDeviceData(string.Format(CultureInfo.InvariantCulture, PlatformResources.ConnectingToClientHost, _host, _port))).ConfigureAwait(false); TcpClient client = new(); #if NETCOREAPP - await client.ConnectAsync(host: clientHost, port: clientPort, cancellationToken); + await client.ConnectAsync(host: _host, port: _port, cancellationToken).ConfigureAwait(false); #else - await client.ConnectAsync(host: clientHost, port: clientPort).WithCancellationAsync(cancellationToken, observeException: true); + await client.ConnectAsync(host: _host, port: _port).WithCancellationAsync(cancellationToken, observeException: true).ConfigureAwait(false); #endif NetworkStream stream = client.GetStream(); return new TcpMessageHandler(client, clientToServerStream: stream, serverToClientStream: stream, FormatterUtilities.CreateFormatter()); } - - private async Task StartTestPlatformServerAsync(int? port, CancellationToken cancellationToken) - { - port ??= 0; - IPEndPoint endPoint = new(IPAddress.Loopback, port.Value); - TcpListener listener = new(endPoint); - - listener.Start(); - try - { - await _outputDevice.DisplayAsync(this, new TextOutputDeviceData(string.Format(CultureInfo.InvariantCulture, PlatformResources.StartingServer, ((IPEndPoint)listener.LocalEndpoint).Port))); - -#if NETCOREAPP - TcpClient client = await listener.AcceptTcpClientAsync(cancellationToken); -#else - TcpClient client = await listener.AcceptTcpClientAsync().WithCancellationAsync(cancellationToken); -#endif - NetworkStream stream = client.GetStream(); - return new TcpMessageHandler(client, clientToServerStream: stream, serverToClientStream: stream, FormatterUtilities.CreateFormatter()); - } - catch (OperationCanceledException oc) when (oc.CancellationToken == cancellationToken) - { - listener.Stop(); - throw; - } - } #pragma warning restore CA1416 public Task IsEnabledAsync() => Task.FromResult(false); diff --git a/src/Platform/Microsoft.Testing.Platform/ServerMode/JsonRpc/PassiveNode.cs b/src/Platform/Microsoft.Testing.Platform/ServerMode/JsonRpc/PassiveNode.cs index 9ab9878ce0..931755233f 100644 --- a/src/Platform/Microsoft.Testing.Platform/ServerMode/JsonRpc/PassiveNode.cs +++ b/src/Platform/Microsoft.Testing.Platform/ServerMode/JsonRpc/PassiveNode.cs @@ -11,7 +11,7 @@ internal sealed class PassiveNode : IDisposable { private readonly IMessageHandlerFactory _messageHandlerFactory; private readonly ITestApplicationCancellationTokenSource _testApplicationCancellationTokenSource; - private readonly IProcessHandler _processHandler; + private readonly IEnvironment _environment; private readonly ILogger _logger; private readonly IAsyncMonitor _messageMonitor; private IMessageHandler? _messageHandler; @@ -19,13 +19,13 @@ internal sealed class PassiveNode : IDisposable public PassiveNode( IMessageHandlerFactory messageHandlerFactory, ITestApplicationCancellationTokenSource testApplicationCancellationTokenSource, - IProcessHandler processHandler, + IEnvironment environment, IAsyncMonitorFactory asyncMonitorFactory, ILogger logger) { _messageHandlerFactory = messageHandlerFactory; _testApplicationCancellationTokenSource = testApplicationCancellationTokenSource; - _processHandler = processHandler; + _environment = environment; _messageMonitor = asyncMonitorFactory.Create(); _logger = logger; } @@ -42,12 +42,12 @@ public void AssertInitialized() public async Task ConnectAsync() { // Create message handler - await _logger.LogDebugAsync("Create message handler"); - _messageHandler = await _messageHandlerFactory.CreateMessageHandlerAsync(_testApplicationCancellationTokenSource.CancellationToken); + await _logger.LogDebugAsync("Create message handler").ConfigureAwait(false); + _messageHandler = await _messageHandlerFactory.CreateMessageHandlerAsync(_testApplicationCancellationTokenSource.CancellationToken).ConfigureAwait(false); // Wait the initial message - await _logger.LogDebugAsync("Wait the initial message"); - RpcMessage? message = await _messageHandler.ReadAsync(_testApplicationCancellationTokenSource.CancellationToken); + await _logger.LogDebugAsync("Wait the initial message").ConfigureAwait(false); + RpcMessage? message = await _messageHandler.ReadAsync(_testApplicationCancellationTokenSource.CancellationToken).ConfigureAwait(false); if (message is null) { return false; @@ -56,12 +56,12 @@ public async Task ConnectAsync() // Log the message if (_logger.IsEnabled(LogLevel.Trace)) { - await _logger.LogTraceAsync(message!.ToString()); + await _logger.LogTraceAsync(message!.ToString()).ConfigureAwait(false); } var requestMessage = (RequestMessage)message; var responseObject = new InitializeResponseArgs( - ProcessId: _processHandler.GetCurrentProcess().Id, + ProcessId: _environment.ProcessId, ServerInfo: new ServerInfo("test-anywhere", Version: PlatformVersion.Version), Capabilities: new ServerCapabilities( new ServerTestingCapabilities( @@ -73,7 +73,7 @@ public async Task ConnectAsync() // This means we're a push node MultiConnectionProvider: true))); - await SendResponseAsync(requestMessage.Id, responseObject, _testApplicationCancellationTokenSource.CancellationToken); + await SendResponseAsync(requestMessage.Id, responseObject, _testApplicationCancellationTokenSource.CancellationToken).ConfigureAwait(false); return true; } @@ -82,9 +82,9 @@ private async Task SendResponseAsync(int reqId, object result, CancellationToken AssertInitialized(); ResponseMessage response = new(reqId, result); - using (await _messageMonitor.LockAsync(cancellationToken)) + using (await _messageMonitor.LockAsync(cancellationToken).ConfigureAwait(false)) { - await _messageHandler.WriteRequestAsync(response, cancellationToken); + await _messageHandler.WriteRequestAsync(response, cancellationToken).ConfigureAwait(false); } } @@ -93,9 +93,9 @@ public async Task SendAttachmentsAsync(TestsAttachments testsAttachments, Cancel AssertInitialized(); NotificationMessage notification = new(JsonRpcMethods.TestingTestUpdatesAttachments, testsAttachments); - using (await _messageMonitor.LockAsync(cancellationToken)) + using (await _messageMonitor.LockAsync(cancellationToken).ConfigureAwait(false)) { - await _messageHandler.WriteRequestAsync(notification, cancellationToken); + await _messageHandler.WriteRequestAsync(notification, cancellationToken).ConfigureAwait(false); } } diff --git a/src/Platform/Microsoft.Testing.Platform/ServerMode/JsonRpc/PerRequestServerDataConsumerService.cs b/src/Platform/Microsoft.Testing.Platform/ServerMode/JsonRpc/PerRequestServerDataConsumerService.cs index 30b26041f2..f61fad28a0 100644 --- a/src/Platform/Microsoft.Testing.Platform/ServerMode/JsonRpc/PerRequestServerDataConsumerService.cs +++ b/src/Platform/Microsoft.Testing.Platform/ServerMode/JsonRpc/PerRequestServerDataConsumerService.cs @@ -92,7 +92,7 @@ private async Task ProcessTestNodeUpdateAsync(TestNodeUpdateMessage update, Canc try { - await _nodeAggregatorSemaphore.WaitAsync(cancellationToken); + await _nodeAggregatorSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false); try { // Note: If there's no changes to aggregate kick off a background task, @@ -108,7 +108,7 @@ private async Task ProcessTestNodeUpdateAsync(TestNodeUpdateMessage update, Canc // Observe possible exceptions try { - await _idleUpdateTask.TimeoutAfterAsync(TimeoutHelper.DefaultHangTimeSpanTimeout, cancellationToken); + await _idleUpdateTask.TimeoutAfterAsync(TimeoutHelper.DefaultHangTimeSpanTimeout, cancellationToken).ConfigureAwait(false); } catch (OperationCanceledException) { @@ -144,14 +144,14 @@ private async Task SendTestNodeUpdatesOnIdleAsync(Guid runId) // When batch timer expire or we're at the end of the session we can unblock the message drain Guard.NotNull(_task); - await Task.WhenAny(_task.Delay(TimeSpan.FromMilliseconds(TestNodeUpdateDelayInMs), cancellationToken), _testSessionEnd.Task); + await Task.WhenAny(_task.Delay(TimeSpan.FromMilliseconds(TestNodeUpdateDelayInMs), cancellationToken), _testSessionEnd.Task).ConfigureAwait(false); if (cancellationToken.IsCancellationRequested) { return; } - await SendTestNodeUpdatesIfNecessaryAsync(runId, cancellationToken); + await SendTestNodeUpdatesIfNecessaryAsync(runId, cancellationToken).ConfigureAwait(false); } catch (OperationCanceledException) when (cancellationToken.IsCancellationRequested) { @@ -165,11 +165,11 @@ private async Task SendTestNodeUpdatesIfNecessaryAsync(Guid runId, CancellationT // and the Task completes, all of the pending updates have been sent. // We synchronize the aggregator access with a separate lock, so that sending // the update message will not block the producers. - await _nodeUpdateSemaphore.WaitAsync(cancellationToken); + await _nodeUpdateSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false); try { TestNodeStateChangedEventArgs? change = null; - await _nodeAggregatorSemaphore.WaitAsync(cancellationToken); + await _nodeAggregatorSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false); try { if (_nodeUpdatesAggregator.HasChanges) @@ -185,7 +185,7 @@ private async Task SendTestNodeUpdatesIfNecessaryAsync(Guid runId, CancellationT if (change is not null) { - await _serverTestHost.SendTestUpdateAsync(change); + await _serverTestHost.SendTestUpdateAsync(change).ConfigureAwait(false); } } finally @@ -200,7 +200,7 @@ public async Task OnTestSessionFinishingAsync(SessionUid sessionUid, Cancellatio { // We signal the test session end so we can complete the flush. _testSessionEnd.SetResult(true); - await GetIdleUpdateTaskAsync().TimeoutAfterAsync(TimeoutHelper.DefaultHangTimeSpanTimeout, cancellationToken); + await GetIdleUpdateTaskAsync().TimeoutAfterAsync(TimeoutHelper.DefaultHangTimeSpanTimeout, cancellationToken).ConfigureAwait(false); } catch (OperationCanceledException) when (cancellationToken.IsCancellationRequested) { @@ -218,7 +218,7 @@ public async Task ConsumeAsync(IDataProducer dataProducer, IData value, Cancella switch (value) { case TestNodeUpdateMessage update: - await ProcessTestNodeUpdateAsync(update, cancellationToken); + await ProcessTestNodeUpdateAsync(update, cancellationToken).ConfigureAwait(false); PopulateTestNodeStatistics(update); break; diff --git a/src/Platform/Microsoft.Testing.Platform/ServerMode/JsonRpc/RpcMessages.cs b/src/Platform/Microsoft.Testing.Platform/ServerMode/JsonRpc/RpcMessages.cs index 941f900ed7..e975f8f469 100644 --- a/src/Platform/Microsoft.Testing.Platform/ServerMode/JsonRpc/RpcMessages.cs +++ b/src/Platform/Microsoft.Testing.Platform/ServerMode/JsonRpc/RpcMessages.cs @@ -6,7 +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 @@ -63,8 +63,6 @@ internal sealed record ClientInfo(string Name, string Version); internal sealed record ClientCapabilities(bool DebuggerProvider); -internal sealed record ClientTestingCapabilities(bool DebuggerProvider); - internal sealed record ServerInfo(string Name, string Version); internal sealed record ServerCapabilities(ServerTestingCapabilities TestingCapabilities); diff --git a/src/Platform/Microsoft.Testing.Platform/ServerMode/JsonRpc/SerializerUtilities.cs b/src/Platform/Microsoft.Testing.Platform/ServerMode/JsonRpc/SerializerUtilities.cs index 97b5733507..4454b98ba9 100644 --- a/src/Platform/Microsoft.Testing.Platform/ServerMode/JsonRpc/SerializerUtilities.cs +++ b/src/Platform/Microsoft.Testing.Platform/ServerMode/JsonRpc/SerializerUtilities.cs @@ -224,7 +224,7 @@ static SerializerUtilities() : $"{testMethodIdentifierProperty.Namespace}.{testMethodIdentifierProperty.TypeName}"; properties["location.method"] = testMethodIdentifierProperty.ParameterTypeFullNames.Length > 0 - ? $"{testMethodIdentifierProperty.MethodName}({string.Join(",", testMethodIdentifierProperty.ParameterTypeFullNames)})" + ? $"{testMethodIdentifierProperty.MethodName}({string.Join(',', testMethodIdentifierProperty.ParameterTypeFullNames)})" : testMethodIdentifierProperty.MethodName; properties["location.method-arity"] = testMethodIdentifierProperty.MethodArity; @@ -338,8 +338,6 @@ static SerializerUtilities() if (property is TimingProperty timingProperty) { - properties["time.start-utc"] = timingProperty.GlobalTiming.StartTime; - properties["time.stop-utc"] = timingProperty.GlobalTiming.EndTime; properties["time.duration-ms"] = timingProperty.GlobalTiming.Duration.TotalMilliseconds; continue; } @@ -354,10 +352,7 @@ static SerializerUtilities() } } - if (!properties.ContainsKey("node-type")) - { - properties["node-type"] = "group"; - } + properties.TryAdd("node-type", "group"); return properties; }); diff --git a/src/Platform/Microsoft.Testing.Platform/ServerMode/JsonRpc/ServerModeManager.cs b/src/Platform/Microsoft.Testing.Platform/ServerMode/JsonRpc/ServerModeManager.cs index c0bc80727b..b9ee389873 100644 --- a/src/Platform/Microsoft.Testing.Platform/ServerMode/JsonRpc/ServerModeManager.cs +++ b/src/Platform/Microsoft.Testing.Platform/ServerMode/JsonRpc/ServerModeManager.cs @@ -7,11 +7,9 @@ namespace Microsoft.Testing.Platform.ServerMode; -internal sealed partial class ServerModeManager : IServerModeManager +internal sealed partial class ServerModeManager { - internal ICommunicationProtocol? CommunicationProtocol { get; set; } - - internal IMessageHandlerFactory Build(IServiceProvider serviceProvider) + internal static IMessageHandlerFactory Build(IServiceProvider serviceProvider) { ICommandLineOptions commandLineService = serviceProvider.GetCommandLineOptions(); @@ -20,34 +18,10 @@ internal IMessageHandlerFactory Build(IServiceProvider serviceProvider) ? int.Parse(clientPortArgs[0], CultureInfo.InvariantCulture) : throw new InvalidOperationException(PlatformResources.MissingClientPortFoJsonRpc); - string? clientHostName; - clientHostName = commandLineService.TryGetOptionArgumentList(PlatformCommandLineProvider.ClientHostOptionKey, out string[]? clientHostArgs) + string clientHostName = commandLineService.TryGetOptionArgumentList(PlatformCommandLineProvider.ClientHostOptionKey, out string[]? clientHostArgs) ? clientHostArgs[0] : "localhost"; - if (CommunicationProtocol is not null) - { - switch (CommunicationProtocol) - { - case JsonRpcTcpServerToSingleClient tcpServerToSingleClientCommunicationProtocol: - { - clientPort ??= tcpServerToSingleClientCommunicationProtocol.ClientPort; - - if (RoslynString.IsNullOrEmpty(clientHostName)) - { - clientHostName = tcpServerToSingleClientCommunicationProtocol.ClientHostName; - } - - break; - } - - default: - { - throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, PlatformResources.UnknownCommunicationProtocolErrorMessage, CommunicationProtocol.GetType())); - } - } - } - return new MessageHandlerFactory(clientHostName, clientPort.Value, serviceProvider.GetOutputDevice()); } } diff --git a/src/Platform/Microsoft.Testing.Platform/ServerMode/JsonRpc/ServerModePerCallOutputDevice.cs b/src/Platform/Microsoft.Testing.Platform/ServerMode/JsonRpc/ServerModePerCallOutputDevice.cs index 1ae054fcbd..406f7419a4 100644 --- a/src/Platform/Microsoft.Testing.Platform/ServerMode/JsonRpc/ServerModePerCallOutputDevice.cs +++ b/src/Platform/Microsoft.Testing.Platform/ServerMode/JsonRpc/ServerModePerCallOutputDevice.cs @@ -15,11 +15,11 @@ internal sealed class ServerModePerCallOutputDevice : IPlatformOutputDevice, IOu { private readonly FileLoggerProvider? _fileLoggerProvider; private readonly IStopPoliciesService _policiesService; - private readonly ConcurrentBag _messages = new(); + private readonly ConcurrentBag _messages = []; private IServerTestHost? _serverTestHost; - private static readonly string[] NewLineStrings = { "\r\n", "\n" }; + private static readonly string[] NewLineStrings = ["\r\n", "\n"]; public ServerModePerCallOutputDevice(FileLoggerProvider? fileLoggerProvider, IStopPoliciesService policiesService) { @@ -41,7 +41,7 @@ internal async Task InitializeAsync(IServerTestHost serverTestHost) foreach (ServerLogMessage message in _messages) { - await LogAsync(message); + await LogAsync(message).ConfigureAwait(false); } _messages.Clear(); @@ -56,30 +56,30 @@ internal async Task InitializeAsync(IServerTestHost serverTestHost) public string Description => nameof(ServerModePerCallOutputDevice); public async Task DisplayAfterSessionEndRunAsync() - => await LogAsync(LogLevel.Trace, PlatformResources.FinishedTestSession, padding: null); + => await LogAsync(LogLevel.Trace, PlatformResources.FinishedTestSession, padding: null).ConfigureAwait(false); public async Task DisplayAsync(IOutputDeviceDataProducer producer, IOutputDeviceData data) { switch (data) { case FormattedTextOutputDeviceData formattedTextOutputDeviceData: - await LogAsync(LogLevel.Information, formattedTextOutputDeviceData.Text, formattedTextOutputDeviceData.Padding); + await LogAsync(LogLevel.Information, formattedTextOutputDeviceData.Text, formattedTextOutputDeviceData.Padding).ConfigureAwait(false); break; case TextOutputDeviceData textOutputDeviceData: - await LogAsync(LogLevel.Information, textOutputDeviceData.Text, padding: null); + await LogAsync(LogLevel.Information, textOutputDeviceData.Text, padding: null).ConfigureAwait(false); break; case WarningMessageOutputDeviceData warningData: - await LogAsync(LogLevel.Warning, warningData.Message, padding: null); + await LogAsync(LogLevel.Warning, warningData.Message, padding: null).ConfigureAwait(false); break; case ErrorMessageOutputDeviceData errorData: - await LogAsync(LogLevel.Error, errorData.Message, padding: null); + await LogAsync(LogLevel.Error, errorData.Message, padding: null).ConfigureAwait(false); break; case ExceptionOutputDeviceData exceptionOutputDeviceData: - await LogAsync(LogLevel.Error, exceptionOutputDeviceData.Exception.ToString(), padding: null); + await LogAsync(LogLevel.Error, exceptionOutputDeviceData.Exception.ToString(), padding: null).ConfigureAwait(false); break; } } @@ -88,7 +88,7 @@ public async Task DisplayBannerAsync(string? bannerMessage) { if (bannerMessage is not null) { - await LogAsync(LogLevel.Debug, bannerMessage, padding: null); + await LogAsync(LogLevel.Debug, bannerMessage, padding: null).ConfigureAwait(false); } } @@ -96,18 +96,18 @@ public async Task DisplayBeforeSessionStartAsync() { if (_fileLoggerProvider is { FileLogger.FileName: { } logFileName }) { - await LogAsync(LogLevel.Trace, string.Format(CultureInfo.InvariantCulture, PlatformResources.StartingTestSessionWithLogFilePath, logFileName), padding: null); + await LogAsync(LogLevel.Trace, string.Format(CultureInfo.InvariantCulture, PlatformResources.StartingTestSessionWithLogFilePath, logFileName), padding: null).ConfigureAwait(false); } else { - await LogAsync(LogLevel.Trace, PlatformResources.StartingTestSession, padding: null); + await LogAsync(LogLevel.Trace, PlatformResources.StartingTestSession, padding: null).ConfigureAwait(false); } } public Task IsEnabledAsync() => Task.FromResult(true); private async Task LogAsync(LogLevel logLevel, string message, int? padding) - => await LogAsync(GetServerLogMessage(logLevel, message, padding)); + => await LogAsync(GetServerLogMessage(logLevel, message, padding)).ConfigureAwait(false); private async Task LogAsync(ServerLogMessage message) { @@ -117,7 +117,7 @@ private async Task LogAsync(ServerLogMessage message) } else { - await _serverTestHost.PushDataAsync(message); + await _serverTestHost.PushDataAsync(message).ConfigureAwait(false); } } @@ -156,7 +156,7 @@ public async Task HandleProcessRoleAsync(TestProcessRole processRole) { await _policiesService.RegisterOnMaxFailedTestsCallbackAsync( async (maxFailedTests, _) => await DisplayAsync( - this, new TextOutputDeviceData(string.Format(CultureInfo.InvariantCulture, PlatformResources.ReachedMaxFailedTestsMessage, maxFailedTests)))); + this, new TextOutputDeviceData(string.Format(CultureInfo.InvariantCulture, PlatformResources.ReachedMaxFailedTestsMessage, maxFailedTests))).ConfigureAwait(false)).ConfigureAwait(false); } } } diff --git a/src/Platform/Microsoft.Testing.Platform/ServerMode/JsonRpc/StreamMessageHandler.cs b/src/Platform/Microsoft.Testing.Platform/ServerMode/JsonRpc/StreamMessageHandler.cs deleted file mode 100644 index c93aabf846..0000000000 --- a/src/Platform/Microsoft.Testing.Platform/ServerMode/JsonRpc/StreamMessageHandler.cs +++ /dev/null @@ -1,161 +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. - -#if NETCOREAPP -using System.Buffers; -#else -using Microsoft.Testing.Platform.Helpers; -#endif - -namespace Microsoft.Testing.Platform.ServerMode; - -internal class StreamMessageHandler : IMessageHandler, IDisposable -{ - private readonly Stream _clientToServerStream; - private readonly Stream _serverToClientStream; - private readonly StreamReader _reader; - private readonly StreamWriter _writer; - private readonly IMessageFormatter _formatter; - private bool _isDisposed; - - public StreamMessageHandler( - Stream clientToServerStream, - Stream serverToClientStream, - IMessageFormatter formatter) - { - _clientToServerStream = clientToServerStream; - _serverToClientStream = serverToClientStream; - _reader = new StreamReader(_clientToServerStream); - _writer = new StreamWriter(_serverToClientStream) - { - // We need to force the NewLine because in Windows and nix different char sequence are used - // https://learn.microsoft.com/dotnet/api/system.io.textwriter.newline?view=net-7.0 - NewLine = "\r\n", - }; - _formatter = formatter; - } - - public virtual async Task ReadAsync(CancellationToken cancellationToken) - { - // Reads an RPC message. - // The message is currently encoded by writing a list of headers - // and then passing a byte stream with the message. - // The headers include the size of the byte stream. - // Content-Length: [content-length]\r\n - // Content-Type: [mime-type]\r\n - // \r\n - // [content]\r\n - while (true) - { - // Content type is not mandatory, and we don't use it. - (int commandSize, string _) = await ReadHeadersAsync(cancellationToken); - - // Most probably connection lost - if (commandSize is -1) - { - return null; - } - -#if NETCOREAPP - char[] commandCharsBuffer = ArrayPool.Shared.Rent(commandSize); - try - { - Memory memoryBuffer = new(commandCharsBuffer, 0, commandSize); - await _reader.ReadBlockAsync(memoryBuffer, cancellationToken); - return _formatter.Deserialize(memoryBuffer); - } - finally - { - ArrayPool.Shared.Return(commandCharsBuffer); - } -#else - char[] commandChars = new char[commandSize]; - await _reader.ReadBlockAsync(commandChars, 0, commandSize).WithCancellationAsync(cancellationToken); - return _formatter.Deserialize(new string(commandChars, 0, commandSize)); -#endif - } - } - - private async Task<(int ContentSize, string ContentType)> ReadHeadersAsync(CancellationToken cancellationToken) - { - int contentSize = -1; - string contentType = string.Empty; - - while (true) - { -#if NET7_0_OR_GREATER - string? line = await _reader.ReadLineAsync(cancellationToken); -#elif NET6_0_OR_GREATER - string? line = await _reader.ReadLineAsync().WaitAsync(cancellationToken); -#else - string? line = await _reader.ReadLineAsync().WithCancellationAsync(cancellationToken); -#endif - if (line is null || (line.Length == 0 && contentSize != -1)) - { - break; - } - - string contentSizeStr = "Content-Length:"; - string contentTypeStr = "Content-Type:"; - if (line.StartsWith(contentSizeStr, StringComparison.OrdinalIgnoreCase)) - { -#if NETCOREAPP - _ = int.TryParse(line.AsSpan()[contentSizeStr.Length..].Trim(), out contentSize); -#else - _ = int.TryParse(line[contentSizeStr.Length..].Trim(), out contentSize); -#endif - } - else if (line.StartsWith(contentTypeStr, StringComparison.OrdinalIgnoreCase)) - { -#if NETCOREAPP - contentType = new(line.AsSpan()[contentTypeStr.Length..].Trim()); -#else - contentType = line[contentTypeStr.Length..].Trim(); -#endif - } - } - - return (contentSize, contentType); - } - - public async Task WriteRequestAsync(RpcMessage message, CancellationToken cancellationToken) - { - string messageStr = await _formatter.SerializeAsync(message); - await _writer.WriteLineAsync($"Content-Length: {Encoding.UTF8.GetByteCount(messageStr)}"); - await _writer.WriteLineAsync("Content-Type: application/testingplatform"); - await _writer.WriteLineAsync(); - await _writer.WriteAsync(messageStr); - await _writer.FlushAsync(cancellationToken); - } - - protected virtual void Dispose(bool disposing) - { - if (!_isDisposed) - { - if (disposing) - { - _reader.Dispose(); - try - { - _writer.Dispose(); - } - catch (InvalidOperationException) - { - // We can exit the server without wait that the streaming activity is completed. - // In that case we can get an InvalidOperationException - // (https://learn.microsoft.com/dotnet/api/system.io.streamwriter.writelineasync?view=net-7.0#system-io-streamwriter-writelineasync(system-string)): - // The stream writer is currently in use by a previous write operation. - } - } - - _isDisposed = true; - } - } - - public void Dispose() - { - // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method - Dispose(disposing: true); - GC.SuppressFinalize(this); - } -} diff --git a/src/Platform/Microsoft.Testing.Platform/ServerMode/JsonRpc/TcpMessageHandler.cs b/src/Platform/Microsoft.Testing.Platform/ServerMode/JsonRpc/TcpMessageHandler.cs index 1ea27abcde..30b690cbd2 100644 --- a/src/Platform/Microsoft.Testing.Platform/ServerMode/JsonRpc/TcpMessageHandler.cs +++ b/src/Platform/Microsoft.Testing.Platform/ServerMode/JsonRpc/TcpMessageHandler.cs @@ -1,44 +1,150 @@ // 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.Net.Sockets; +#if !NETCOREAPP +using Microsoft.Testing.Platform.Helpers; +#endif + namespace Microsoft.Testing.Platform.ServerMode; internal sealed class TcpMessageHandler( TcpClient client, Stream clientToServerStream, Stream serverToClientStream, - IMessageFormatter formatter) : StreamMessageHandler(clientToServerStream, serverToClientStream, formatter) + IMessageFormatter formatter) : IMessageHandler, IDisposable { private readonly TcpClient _client = client; + private readonly StreamReader _reader = new(clientToServerStream); + private readonly StreamWriter _writer = new(serverToClientStream) + { + // We need to force the NewLine because in Windows and nix different char sequence are used + // https://learn.microsoft.com/dotnet/api/system.io.textwriter.newline?view=net-7.0 + NewLine = "\r\n", + }; - public override async Task ReadAsync(CancellationToken cancellationToken) + private readonly IMessageFormatter _formatter = formatter; + + public async Task ReadAsync(CancellationToken cancellationToken) { try { - return await base.ReadAsync(cancellationToken); + // Reads an RPC message. + // The message is currently encoded by writing a list of headers + // and then passing a byte stream with the message. + // The headers include the size of the byte stream. + // Content-Length: [content-length]\r\n + // Content-Type: [mime-type]\r\n + // \r\n + // [content]\r\n + while (true) + { + int commandSize = await ReadHeadersAsync(cancellationToken).ConfigureAwait(false); + + // Most probably connection lost + if (commandSize is -1) + { + return null; + } + +#if NETCOREAPP + char[] commandCharsBuffer = ArrayPool.Shared.Rent(commandSize); + try + { + Memory memoryBuffer = new(commandCharsBuffer, 0, commandSize); + await _reader.ReadBlockAsync(memoryBuffer, cancellationToken).ConfigureAwait(false); + return _formatter.Deserialize(memoryBuffer); + } + finally + { + ArrayPool.Shared.Return(commandCharsBuffer); + } +#else + char[] commandChars = new char[commandSize]; + await _reader.ReadBlockAsync(commandChars, 0, commandSize).WithCancellationAsync(cancellationToken).ConfigureAwait(false); + return _formatter.Deserialize(new string(commandChars, 0, commandSize)); +#endif + } } // Client close the connection in an unexpected way - catch (Exception ex) + catch (Exception ex) when + (ex is + SocketException { SocketErrorCode: SocketError.ConnectionReset } or + IOException + { + InnerException: SocketException { SocketErrorCode: SocketError.ConnectionReset } + }) { - switch (ex) + return null; + } + } + + private async Task ReadHeadersAsync(CancellationToken cancellationToken) + { + int contentSize = -1; + + while (true) + { +#if NET7_0_OR_GREATER + string? line = await _reader.ReadLineAsync(cancellationToken).ConfigureAwait(false); +#elif NET6_0_OR_GREATER + string? line = await _reader.ReadLineAsync().WaitAsync(cancellationToken).ConfigureAwait(false); +#else + string? line = await _reader.ReadLineAsync().WithCancellationAsync(cancellationToken).ConfigureAwait(false); +#endif + if (line is null || (line.Length == 0 && contentSize != -1)) { - case SocketException { SocketErrorCode: SocketError.ConnectionReset }: - case IOException { InnerException: SocketException { SocketErrorCode: SocketError.ConnectionReset } }: - return null; - default: - throw; + break; + } + + const string ContentLengthHeaderName = "Content-Length:"; + // Content type is not mandatory, and we don't use it. + if (line.StartsWith(ContentLengthHeaderName, StringComparison.OrdinalIgnoreCase)) + { +#if NETCOREAPP + _ = int.TryParse(line.AsSpan()[ContentLengthHeaderName.Length..].Trim(), out contentSize); +#else + _ = int.TryParse(line[ContentLengthHeaderName.Length..].Trim(), out contentSize); +#endif } } + + return contentSize; + } + + public async Task WriteRequestAsync(RpcMessage message, CancellationToken cancellationToken) + { + string messageStr = await _formatter.SerializeAsync(message).ConfigureAwait(false); + await _writer.WriteLineAsync($"Content-Length: {Encoding.UTF8.GetByteCount(messageStr)}").ConfigureAwait(false); + await _writer.WriteLineAsync("Content-Type: application/testingplatform").ConfigureAwait(false); + await _writer.WriteLineAsync().ConfigureAwait(false); + await _writer.WriteAsync(messageStr).ConfigureAwait(false); + await _writer.FlushAsync(cancellationToken).ConfigureAwait(false); } - protected override void Dispose(bool disposing) + public void Dispose() { - base.Dispose(disposing); + _reader.Dispose(); + + try + { + _writer.Dispose(); + } + catch (InvalidOperationException) + { + // We can exit the server without wait that the streaming activity is completed. + // In that case we can get an InvalidOperationException + // (https://learn.microsoft.com/dotnet/api/system.io.streamwriter.writelineasync?view=net-7.0#system-io-streamwriter-writelineasync(system-string)): + // The stream writer is currently in use by a previous write operation. + } + #pragma warning disable CA1416 // Validate platform compatibility - _client.Close(); + _client.Dispose(); #pragma warning restore CA1416 } } diff --git a/src/Platform/Microsoft.Testing.Platform/ServerMode/JsonRpc/TestNodeStateChangeAggregator.cs b/src/Platform/Microsoft.Testing.Platform/ServerMode/JsonRpc/TestNodeStateChangeAggregator.cs index 0c26a1c096..91a23ad4f7 100644 --- a/src/Platform/Microsoft.Testing.Platform/ServerMode/JsonRpc/TestNodeStateChangeAggregator.cs +++ b/src/Platform/Microsoft.Testing.Platform/ServerMode/JsonRpc/TestNodeStateChangeAggregator.cs @@ -29,6 +29,6 @@ public void OnStateChange(TestNodeUpdateMessage stateChangedMessage) => _stateChanges.Add(stateChangedMessage); public TestNodeStateChangedEventArgs BuildAggregatedChange() - => new(RunId, _stateChanges.ToArray()); + => new(RunId, [.. _stateChanges]); } } diff --git a/src/Platform/Microsoft.Testing.Platform/Services/CurrentTestApplicationModuleInfo.cs b/src/Platform/Microsoft.Testing.Platform/Services/CurrentTestApplicationModuleInfo.cs index 8af8270867..8f8fffd0ed 100644 --- a/src/Platform/Microsoft.Testing.Platform/Services/CurrentTestApplicationModuleInfo.cs +++ b/src/Platform/Microsoft.Testing.Platform/Services/CurrentTestApplicationModuleInfo.cs @@ -85,20 +85,18 @@ public string GetProcessPath() => GetProcessPath(_environment, _process, throwOnNull: true)!; private static string? GetProcessPath(IEnvironment environment, IProcessHandler process, bool throwOnNull = false) -#if NETCOREAPP { +#if NETCOREAPP string? processPath = environment.ProcessPath; - ApplicationStateGuard.Ensure(processPath is not null || !throwOnNull); - - return processPath; - } #else - { using IProcess currentProcess = process.GetCurrentProcess(); - return currentProcess.MainModule.FileName; - } + string? processPath = currentProcess.MainModule?.FileName; #endif + ApplicationStateGuard.Ensure(processPath is not null || !throwOnNull); + return processPath; + } + public ExecutableInfo GetCurrentExecutableInfo() { bool isDotnetMuxer = IsCurrentTestApplicationHostDotnetMuxer; diff --git a/src/Platform/Microsoft.Testing.Platform/Services/ExecutableInfo.cs b/src/Platform/Microsoft.Testing.Platform/Services/ExecutableInfo.cs index 80b35d1441..17b6023e2b 100644 --- a/src/Platform/Microsoft.Testing.Platform/Services/ExecutableInfo.cs +++ b/src/Platform/Microsoft.Testing.Platform/Services/ExecutableInfo.cs @@ -12,5 +12,5 @@ internal sealed class ExecutableInfo(string filePath, IEnumerable argume public string Workspace { get; } = workspace; public override string ToString() - => $"Process: {FilePath}, Arguments: {string.Join(" ", Arguments)}, Workspace: {Workspace}"; + => $"Process: {FilePath}, Arguments: {string.Join(' ', Arguments)}, Workspace: {Workspace}"; } diff --git a/src/Platform/Microsoft.Testing.Platform/Services/IPlatformInformation.cs b/src/Platform/Microsoft.Testing.Platform/Services/IPlatformInformation.cs index 8c8f1302fa..98959a6647 100644 --- a/src/Platform/Microsoft.Testing.Platform/Services/IPlatformInformation.cs +++ b/src/Platform/Microsoft.Testing.Platform/Services/IPlatformInformation.cs @@ -64,7 +64,7 @@ public PlatformInformation() } } - public string Name { get; } = "Microsoft.Testing.Platform"; + public string Name => "Microsoft.Testing.Platform"; public DateTimeOffset? BuildDate { get; } diff --git a/src/Platform/Microsoft.Testing.Platform/Services/ServiceProvider.cs b/src/Platform/Microsoft.Testing.Platform/Services/ServiceProvider.cs index 556e89cc09..c85d18a92b 100644 --- a/src/Platform/Microsoft.Testing.Platform/Services/ServiceProvider.cs +++ b/src/Platform/Microsoft.Testing.Platform/Services/ServiceProvider.cs @@ -17,10 +17,12 @@ internal sealed class ServiceProvider : IServiceProvider, ICloneable public bool AllowTestAdapterFrameworkRegistration { get; set; } +#pragma warning disable CS0618 // Type or member is obsolete private static Type[] InternalOnlyExtensions => [ // TestHost typeof(ITestApplicationLifecycleCallbacks), + typeof(ITestHostApplicationLifetime), typeof(IDataConsumer), typeof(ITestSessionLifetimeHandler), @@ -28,6 +30,7 @@ internal sealed class ServiceProvider : IServiceProvider, ICloneable typeof(ITestHostEnvironmentVariableProvider), typeof(ITestHostProcessLifetimeHandler) ]; +#pragma warning restore CS0618 // Type or member is obsolete public void AddService(object service, bool throwIfSameInstanceExit = true) { diff --git a/src/Platform/Microsoft.Testing.Platform/Services/StopPoliciesService.cs b/src/Platform/Microsoft.Testing.Platform/Services/StopPoliciesService.cs index c69e617b8a..b9b2aed89b 100644 --- a/src/Platform/Microsoft.Testing.Platform/Services/StopPoliciesService.cs +++ b/src/Platform/Microsoft.Testing.Platform/Services/StopPoliciesService.cs @@ -19,7 +19,7 @@ public StopPoliciesService(ITestApplicationCancellationTokenSource testApplicati #pragma warning disable VSTHRD101 // Avoid unsupported async delegates // Note: If cancellation already requested, Register will still invoke the callback. - testApplicationCancellationTokenSource.CancellationToken.Register(async () => await ExecuteAbortCallbacksAsync()); + testApplicationCancellationTokenSource.CancellationToken.Register(async () => await ExecuteAbortCallbacksAsync().ConfigureAwait(false)); #pragma warning restore VSTHRD101 // Avoid unsupported async delegates } @@ -31,7 +31,7 @@ public StopPoliciesService(ITestApplicationCancellationTokenSource testApplicati private static void RegisterCallback(ref BlockingCollection? callbacks, T callback) #pragma warning disable CA1416 // Validate platform compatibility - => (callbacks ??= new()).Add(callback); + => (callbacks ??= []).Add(callback); #pragma warning restore CA1416 public async Task ExecuteMaxFailedTestsCallbacksAsync(int maxFailedTests, CancellationToken cancellationToken) @@ -47,7 +47,7 @@ public async Task ExecuteMaxFailedTestsCallbacksAsync(int maxFailedTests, Cancel { // For now, we are fine if the callback crashed us. It shouldn't happen for our // current usage anyway and the APIs around this are all internal for now. - await callback.Invoke(maxFailedTests, cancellationToken); + await callback.Invoke(maxFailedTests, cancellationToken).ConfigureAwait(false); } } @@ -64,7 +64,7 @@ public async Task ExecuteAbortCallbacksAsync() { // For now, we are fine if the callback crashed us. It shouldn't happen for our // current usage anyway and the APIs around this are all internal for now. - await callback.Invoke(); + await callback.Invoke().ConfigureAwait(false); } } @@ -77,7 +77,7 @@ public async Task RegisterOnMaxFailedTestsCallbackAsync(Func callback) { if (IsAbortTriggered) { - await callback(); + await callback().ConfigureAwait(false); } RegisterCallback(ref _abortCallbacks, callback); diff --git a/src/Platform/Microsoft.Testing.Platform/Services/TestApplicationResult.cs b/src/Platform/Microsoft.Testing.Platform/Services/TestApplicationResult.cs index 6539fae697..cf3835cee0 100644 --- a/src/Platform/Microsoft.Testing.Platform/Services/TestApplicationResult.cs +++ b/src/Platform/Microsoft.Testing.Platform/Services/TestApplicationResult.cs @@ -17,7 +17,8 @@ internal sealed class TestApplicationResult : ITestApplicationProcessExitCode, I private readonly ICommandLineOptions _commandLineOptions; private readonly IEnvironment _environment; private readonly IStopPoliciesService _policiesService; - private readonly List _failedTests = []; + private readonly bool _isDiscovery; + private int _failedTestsCount; private int _totalRanTests; private bool _testAdapterTestSessionFailure; @@ -31,13 +32,14 @@ public TestApplicationResult( _commandLineOptions = commandLineOptions; _environment = environment; _policiesService = policiesService; + _isDiscovery = _commandLineOptions.IsOptionSet(PlatformCommandLineProvider.DiscoverTestsOptionKey); } /// - public string Uid { get; } = nameof(TestApplicationResult); + public string Uid => nameof(TestApplicationResult); /// - public string Version { get; } = AppVersion.DefaultSemVer; + public string Version => AppVersion.DefaultSemVer; /// public string DisplayName { get; } = PlatformResources.TestApplicationResultDisplayName; @@ -68,10 +70,10 @@ public Task ConsumeAsync(IDataProducer dataProducer, IData value, CancellationTo if (Array.IndexOf(TestNodePropertiesCategories.WellKnownTestNodeTestRunOutcomeFailedProperties, executionState.GetType()) != -1) { - _failedTests.Add(message.TestNode); + _failedTestsCount++; } - if (_commandLineOptions.IsOptionSet(PlatformCommandLineProvider.DiscoverTestsOptionKey) + if (_isDiscovery && Array.IndexOf(TestNodePropertiesCategories.WellKnownTestNodeDiscoveredProperties, executionState.GetType()) != -1) { _totalRanTests++; @@ -89,7 +91,7 @@ public int GetProcessExitCode() int exitCode = ExitCodes.Success; exitCode = exitCode == ExitCodes.Success && _policiesService.IsMaxFailedTestsTriggered ? ExitCodes.TestExecutionStoppedForMaxFailedTests : exitCode; exitCode = exitCode == ExitCodes.Success && _testAdapterTestSessionFailure ? ExitCodes.TestAdapterTestSessionFailure : exitCode; - exitCode = exitCode == ExitCodes.Success && _failedTests.Count > 0 ? ExitCodes.AtLeastOneTestFailed : exitCode; + exitCode = exitCode == ExitCodes.Success && _failedTestsCount > 0 ? ExitCodes.AtLeastOneTestFailed : exitCode; exitCode = exitCode == ExitCodes.Success && _policiesService.IsAbortTriggered ? ExitCodes.TestSessionAborted : exitCode; exitCode = exitCode == ExitCodes.Success && _totalRanTests == 0 ? ExitCodes.ZeroTests : exitCode; @@ -123,9 +125,9 @@ public async Task SetTestAdapterTestSessionFailureAsync(string errorMessage) { TestAdapterTestSessionFailureErrorMessage = errorMessage; _testAdapterTestSessionFailure = true; - await _outputService.DisplayAsync(this, new ErrorMessageOutputDeviceData(errorMessage)); + await _outputService.DisplayAsync(this, new ErrorMessageOutputDeviceData(errorMessage)).ConfigureAwait(false); } public Statistics GetStatistics() - => new() { TotalRanTests = _totalRanTests, TotalFailedTests = _failedTests.Count }; + => new() { TotalRanTests = _totalRanTests, TotalFailedTests = _failedTestsCount }; } diff --git a/src/Platform/Microsoft.Testing.Platform/Telemetry/ExtensionInformationCollector.cs b/src/Platform/Microsoft.Testing.Platform/Telemetry/ExtensionInformationCollector.cs index ff91fab992..dbff0964d6 100644 --- a/src/Platform/Microsoft.Testing.Platform/Telemetry/ExtensionInformationCollector.cs +++ b/src/Platform/Microsoft.Testing.Platform/Telemetry/ExtensionInformationCollector.cs @@ -25,7 +25,7 @@ public static async Task CollectAndSerializeToJsonAsync(ServiceProvider { if (service is IExtension extension) { - extensionsInformation.Add(new ExtensionInformation(Sha256Hasher.HashWithNormalizedCasing(extension.Uid), extension.Version, await extension.IsEnabledAsync())); + extensionsInformation.Add(new ExtensionInformation(Sha256Hasher.HashWithNormalizedCasing(extension.Uid), extension.Version, await extension.IsEnabledAsync().ConfigureAwait(false))); } if (service is MessageBusProxy messageBus) @@ -34,7 +34,7 @@ public static async Task CollectAndSerializeToJsonAsync(ServiceProvider { if (dataConsumer is IExtension extension1) { - extensionsInformation.Add(new ExtensionInformation(Sha256Hasher.HashWithNormalizedCasing(extension1.Uid), extension1.Version, await extension1.IsEnabledAsync())); + extensionsInformation.Add(new ExtensionInformation(Sha256Hasher.HashWithNormalizedCasing(extension1.Uid), extension1.Version, await extension1.IsEnabledAsync().ConfigureAwait(false))); } } } diff --git a/src/Platform/Microsoft.Testing.Platform/Telemetry/ServerTelemetry.cs b/src/Platform/Microsoft.Testing.Platform/Telemetry/ServerTelemetry.cs index 01646f0f4e..27a2b92a74 100644 --- a/src/Platform/Microsoft.Testing.Platform/Telemetry/ServerTelemetry.cs +++ b/src/Platform/Microsoft.Testing.Platform/Telemetry/ServerTelemetry.cs @@ -13,9 +13,9 @@ internal sealed class ServerTelemetry(IServerTestHost serverTestHost) : ITelemet public async Task LogEventAsync(string eventName, IDictionary paramsMap) { TelemetryEventArgs logMessage = new(eventName, paramsMap); - await PushTelemetryToServerTestHostAsync(logMessage); + await PushTelemetryToServerTestHostAsync(logMessage).ConfigureAwait(false); } private async Task PushTelemetryToServerTestHostAsync(TelemetryEventArgs telemetryEvent) - => await _serverTestHost.SendTelemetryEventUpdateAsync(telemetryEvent); + => await _serverTestHost.SendTelemetryEventUpdateAsync(telemetryEvent).ConfigureAwait(false); } diff --git a/src/Platform/Microsoft.Testing.Platform/Telemetry/TelemetryManager.cs b/src/Platform/Microsoft.Testing.Platform/Telemetry/TelemetryManager.cs index c96926d870..eeaeb0fa56 100644 --- a/src/Platform/Microsoft.Testing.Platform/Telemetry/TelemetryManager.cs +++ b/src/Platform/Microsoft.Testing.Platform/Telemetry/TelemetryManager.cs @@ -35,23 +35,23 @@ public async Task BuildAsync(ServiceProvider serviceProvide bool isTelemetryOptedOut = !testApplicationOptions.EnableTelemetry; ILogger logger = loggerFactory.CreateLogger(); - await logger.LogDebugAsync($"TestApplicationOptions.EnableTelemetry: {testApplicationOptions.EnableTelemetry}"); + await logger.LogDebugAsync($"TestApplicationOptions.EnableTelemetry: {testApplicationOptions.EnableTelemetry}").ConfigureAwait(false); // 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.LogDebugAsync($"{EnvironmentVariableConstants.TESTINGPLATFORM_TELEMETRY_OPTOUT} environment variable: '{telemetryOptOut}'"); + await logger.LogDebugAsync($"{EnvironmentVariableConstants.TESTINGPLATFORM_TELEMETRY_OPTOUT} environment variable: '{telemetryOptOut}'").ConfigureAwait(false); isTelemetryOptedOut = (telemetryOptOut is "1" or "true") || isTelemetryOptedOut; string? cli_telemetryOptOut = environment.GetEnvironmentVariable(EnvironmentVariableConstants.DOTNET_CLI_TELEMETRY_OPTOUT); - await logger.LogDebugAsync($"{EnvironmentVariableConstants.DOTNET_CLI_TELEMETRY_OPTOUT} environment variable: '{cli_telemetryOptOut}'"); + await logger.LogDebugAsync($"{EnvironmentVariableConstants.DOTNET_CLI_TELEMETRY_OPTOUT} environment variable: '{cli_telemetryOptOut}'").ConfigureAwait(false); isTelemetryOptedOut = (cli_telemetryOptOut is "1" or "true") || isTelemetryOptedOut; - await logger.LogDebugAsync($"Telemetry is '{(!isTelemetryOptedOut ? "ENABLED" : "DISABLED")}'"); + await logger.LogDebugAsync($"Telemetry is '{(!isTelemetryOptedOut ? "ENABLED" : "DISABLED")}'").ConfigureAwait(false); if (!isTelemetryOptedOut && _telemetryFactory is not null) { - await ShowTelemetryBannerFirstNoticeAsync(serviceProvider, logger, environment); + await ShowTelemetryBannerFirstNoticeAsync(serviceProvider, logger, environment).ConfigureAwait(false); } serviceProvider.TryAddService(new TelemetryInformation(!isTelemetryOptedOut, TelemetryProperties.VersionValue)); @@ -62,7 +62,7 @@ public async Task BuildAsync(ServiceProvider serviceProvide if (!isTelemetryOptedOut) { - await logger.LogDebugAsync($"Telemetry collector provider: '{telemetryCollector.GetType()}'"); + await logger.LogDebugAsync($"Telemetry collector provider: '{telemetryCollector.GetType()}'").ConfigureAwait(false); } return telemetryCollector; @@ -75,11 +75,11 @@ private async Task ShowTelemetryBannerFirstNoticeAsync(ServiceProvider servicePr bool doNotShowLogo = commandLineOptions.IsOptionSet(PlatformCommandLineProvider.NoBannerOptionKey); string? noBannerEnvVar = environment.GetEnvironmentVariable(EnvironmentVariableConstants.TESTINGPLATFORM_NOBANNER); - await logger.LogDebugAsync($"{EnvironmentVariableConstants.TESTINGPLATFORM_NOBANNER} environment variable: '{noBannerEnvVar}'"); + await logger.LogDebugAsync($"{EnvironmentVariableConstants.TESTINGPLATFORM_NOBANNER} environment variable: '{noBannerEnvVar}'").ConfigureAwait(false); doNotShowLogo = (noBannerEnvVar is "1" or "true") || doNotShowLogo; string? dotnetNoLogoEnvVar = environment.GetEnvironmentVariable(EnvironmentVariableConstants.DOTNET_NOLOGO); - await logger.LogDebugAsync($"{EnvironmentVariableConstants.DOTNET_NOLOGO} environment variable: '{dotnetNoLogoEnvVar}'"); + await logger.LogDebugAsync($"{EnvironmentVariableConstants.DOTNET_NOLOGO} environment variable: '{dotnetNoLogoEnvVar}'").ConfigureAwait(false); doNotShowLogo = (dotnetNoLogoEnvVar is "1" or "true") || doNotShowLogo; if (doNotShowLogo) @@ -103,7 +103,7 @@ private async Task ShowTelemetryBannerFirstNoticeAsync(ServiceProvider servicePr bool sentinelIsNotPresent = RoslynString.IsNullOrWhiteSpace(directory) - || !fileSystem.Exists(Path.Combine(directory, fileName)); + || !fileSystem.ExistFile(Path.Combine(directory, fileName)); if (!sentinelIsNotPresent) { @@ -111,7 +111,7 @@ private async Task ShowTelemetryBannerFirstNoticeAsync(ServiceProvider servicePr } IOutputDevice outputDevice = serviceProvider.GetOutputDevice(); - await outputDevice.DisplayAsync(this, new TextOutputDeviceData(PlatformResources.TelemetryNotice)); + await outputDevice.DisplayAsync(this, new TextOutputDeviceData(PlatformResources.TelemetryNotice)).ConfigureAwait(false); string? path = null; try @@ -130,7 +130,7 @@ private async Task ShowTelemetryBannerFirstNoticeAsync(ServiceProvider servicePr } catch (Exception exception) when (exception is IOException or SystemException) { - await logger.LogErrorAsync($"Could not write sentinel file for telemetry to path,'{path ?? ""}'.", exception); + await logger.LogErrorAsync($"Could not write sentinel file for telemetry to path,'{path ?? ""}'.", exception).ConfigureAwait(false); } } diff --git a/src/Platform/Microsoft.Testing.Platform/TestHost/ITestApplicationLifecycleCallbacks.cs b/src/Platform/Microsoft.Testing.Platform/TestHost/ITestHostApplicationLifetime.cs similarity index 66% rename from src/Platform/Microsoft.Testing.Platform/TestHost/ITestApplicationLifecycleCallbacks.cs rename to src/Platform/Microsoft.Testing.Platform/TestHost/ITestHostApplicationLifetime.cs index e81a4b7128..e21bb9ee44 100644 --- a/src/Platform/Microsoft.Testing.Platform/TestHost/ITestApplicationLifecycleCallbacks.cs +++ b/src/Platform/Microsoft.Testing.Platform/TestHost/ITestHostApplicationLifetime.cs @@ -6,6 +6,7 @@ namespace Microsoft.Testing.Platform.Extensions.TestHost; /// /// Represents the interface for test application lifecycle callbacks. /// +[Obsolete("Use ITestHostApplicationLifetime instead. This interface will be removed in v2.")] public interface ITestApplicationLifecycleCallbacks : ITestHostExtension { /// @@ -23,3 +24,13 @@ public interface ITestApplicationLifecycleCallbacks : ITestHostExtension /// A task representing the asynchronous operation. Task AfterRunAsync(int exitCode, CancellationToken cancellation); } + +/// +/// Represents the interface for test application lifecycle callbacks. +/// +#pragma warning disable CS0618 // Type or member is obsolete +public interface ITestHostApplicationLifetime : ITestHostExtension, ITestApplicationLifecycleCallbacks +#pragma warning restore CS0618 // Type or member is obsolete +{ + // In v2, move BeforeRunAsync and AfterRunAsync to ITestHostApplicationLifetime directly +} diff --git a/src/Platform/Microsoft.Testing.Platform/TestHost/ITestHostManager.cs b/src/Platform/Microsoft.Testing.Platform/TestHost/ITestHostManager.cs index 4be1bccb84..4fd70a7844 100644 --- a/src/Platform/Microsoft.Testing.Platform/TestHost/ITestHostManager.cs +++ b/src/Platform/Microsoft.Testing.Platform/TestHost/ITestHostManager.cs @@ -15,8 +15,15 @@ public interface ITestHostManager /// Adds a test application lifecycle callbacks. /// /// The factory method for creating the test application lifecycle callbacks. + [Obsolete("Use 'AddTestHostApplicationLifetime' instead.")] void AddTestApplicationLifecycleCallbacks(Func testApplicationLifecycleCallbacks); + /// + /// Adds a test application lifecycle callbacks. + /// + /// The factory method for creating the test host application lifetime callbacks. + void AddTestHostApplicationLifetime(Func testHostApplicationLifetimeFactory); + /// /// Adds a data consumer. /// diff --git a/src/Platform/Microsoft.Testing.Platform/TestHost/TestHostManager.cs b/src/Platform/Microsoft.Testing.Platform/TestHost/TestHostManager.cs index 684e3cfeec..ff714a33e0 100644 --- a/src/Platform/Microsoft.Testing.Platform/TestHost/TestHostManager.cs +++ b/src/Platform/Microsoft.Testing.Platform/TestHost/TestHostManager.cs @@ -16,7 +16,9 @@ internal sealed class TestHostManager : ITestHostManager private readonly List _factoryOrdering = []; // Exposed extension points +#pragma warning disable CS0618 // Type or member is obsolete private readonly List> _testApplicationLifecycleCallbacksFactories = []; +#pragma warning restore CS0618 // Type or member is obsolete private readonly List> _dataConsumerFactories = []; private readonly List> _testSessionLifetimeHandlerFactories = []; private readonly List _dataConsumersCompositeServiceFactories = []; @@ -47,9 +49,9 @@ internal async Task> TryBuildTestAdapterInvo ITestFrameworkInvoker testAdapterInvoke = _testFrameworkInvokerFactory(serviceProvider); // We initialize only if enabled - if (await testAdapterInvoke.IsEnabledAsync()) + if (await testAdapterInvoke.IsEnabledAsync().ConfigureAwait(false)) { - await testAdapterInvoke.TryInitializeAsync(); + await testAdapterInvoke.TryInitializeAsync().ConfigureAwait(false); return ActionResult.Ok(testAdapterInvoke); } @@ -78,9 +80,9 @@ internal async Task> TryBuildTestExecu ITestExecutionFilterFactory testExecutionFilterFactory = _testExecutionFilterFactory(serviceProvider); // We initialize only if enabled - if (await testExecutionFilterFactory.IsEnabledAsync()) + if (await testExecutionFilterFactory.IsEnabledAsync().ConfigureAwait(false)) { - await testExecutionFilterFactory.TryInitializeAsync(); + await testExecutionFilterFactory.TryInitializeAsync().ConfigureAwait(false); return ActionResult.Ok(testExecutionFilterFactory); } @@ -88,12 +90,22 @@ internal async Task> TryBuildTestExecu return ActionResult.Fail(); } + [Obsolete] public void AddTestApplicationLifecycleCallbacks(Func testApplicationLifecycleCallbacks) { Guard.NotNull(testApplicationLifecycleCallbacks); _testApplicationLifecycleCallbacksFactories.Add(testApplicationLifecycleCallbacks); } + public void AddTestHostApplicationLifetime(Func testHostApplicationLifetime) + { + Guard.NotNull(testHostApplicationLifetime); +#pragma warning disable CS0612 // Type or member is obsolete + _testApplicationLifecycleCallbacksFactories.Add(testHostApplicationLifetime); +#pragma warning restore CS0612 // Type or member is obsolete + } + +#pragma warning disable CS0618 // Type or member is obsolete internal async Task BuildTestApplicationLifecycleCallbackAsync(ServiceProvider serviceProvider) { List testApplicationLifecycleCallbacks = []; @@ -102,24 +114,21 @@ internal async Task BuildTestApplicationLi ITestApplicationLifecycleCallbacks service = testApplicationLifecycleCallbacksFactory(serviceProvider); // Check if we have already extensions of the same type with same id registered - if (testApplicationLifecycleCallbacks.Any(x => x.Uid == service.Uid)) - { - ITestApplicationLifecycleCallbacks currentRegisteredExtension = testApplicationLifecycleCallbacks.Single(x => x.Uid == service.Uid); - throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, PlatformResources.ExtensionWithSameUidAlreadyRegisteredErrorMessage, service.Uid, currentRegisteredExtension.GetType())); - } + testApplicationLifecycleCallbacks.ValidateUniqueExtension(service); // We initialize only if enabled - if (await service.IsEnabledAsync()) + if (await service.IsEnabledAsync().ConfigureAwait(false)) { - await service.TryInitializeAsync(); + await service.TryInitializeAsync().ConfigureAwait(false); // Register the extension for usage testApplicationLifecycleCallbacks.Add(service); } } - return testApplicationLifecycleCallbacks.ToArray(); + return [.. testApplicationLifecycleCallbacks]; } +#pragma warning restore CS0618 // Type or member is obsolete public void AddDataConsumer(Func dataConsumerFactory) { @@ -149,16 +158,12 @@ public void AddDataConsumer(CompositeExtensionFactory compositeServiceFact IDataConsumer service = dataConsumerFactory(serviceProvider); // Check if we have already extensions of the same type with same id registered - if (dataConsumers.Any(x => x.Consumer.Uid == service.Uid)) - { - (IExtension consumer, int order) = dataConsumers.Single(x => x.Consumer.Uid == service.Uid); - throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, PlatformResources.ExtensionWithSameUidAlreadyRegisteredErrorMessage, service.Uid, consumer.GetType())); - } + dataConsumers.ValidateUniqueExtension(service, x => x.Consumer); // We initialize only if enabled - if (await service.IsEnabledAsync()) + if (await service.IsEnabledAsync().ConfigureAwait(false)) { - await service.TryInitializeAsync(); + await service.TryInitializeAsync().ConfigureAwait(false); // Register the extension for usage dataConsumers.Add((service, _factoryOrdering.IndexOf(dataConsumerFactory))); @@ -179,16 +184,12 @@ public void AddDataConsumer(CompositeExtensionFactory compositeServiceFact var instance = (IExtension)compositeFactoryInstance.GetInstance(serviceProvider); // Check if we have already extensions of the same type with same id registered - if (dataConsumers.Any(x => x.Consumer.Uid == instance.Uid)) - { - (IExtension consumer, int _) = dataConsumers.Single(x => x.Consumer.Uid == instance.Uid); - throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, PlatformResources.ExtensionWithSameUidAlreadyRegisteredErrorMessage, instance.Uid, consumer.GetType())); - } + dataConsumers.ValidateUniqueExtension(instance, x => x.Consumer); // We initialize only if enabled - if (await instance.IsEnabledAsync()) + if (await instance.IsEnabledAsync().ConfigureAwait(false)) { - await instance.TryInitializeAsync(); + await instance.TryInitializeAsync().ConfigureAwait(false); } // Add to the list of shared singletons @@ -199,7 +200,7 @@ public void AddDataConsumer(CompositeExtensionFactory compositeServiceFact var extension = (IExtension)compositeFactoryInstance.GetInstance(); // We register the extension only if enabled - if (await extension.IsEnabledAsync()) + if (await extension.IsEnabledAsync().ConfigureAwait(false)) { if (extension is IDataConsumer consumer) { @@ -212,7 +213,7 @@ public void AddDataConsumer(CompositeExtensionFactory compositeServiceFact } } - return dataConsumers.ToArray(); + return [.. dataConsumers]; } public void AddTestSessionLifetimeHandle(Func testSessionLifetimeHandleFactory) @@ -243,16 +244,12 @@ public void AddTestSessionLifetimeHandle(CompositeExtensionFactory composi ITestSessionLifetimeHandler service = testSessionLifetimeHandlerFactory(serviceProvider); // Check if we have already extensions of the same type with same id registered - if (testSessionLifetimeHandlers.Any(x => x.TestSessionLifetimeHandler.Uid == service.Uid)) - { - (IExtension testSessionLifetimeHandler, int _) = testSessionLifetimeHandlers.Single(x => x.TestSessionLifetimeHandler.Uid == service.Uid); - throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, PlatformResources.ExtensionWithSameUidAlreadyRegisteredErrorMessage, service.Uid, testSessionLifetimeHandler.GetType())); - } + testSessionLifetimeHandlers.ValidateUniqueExtension(service, x => x.TestSessionLifetimeHandler); // We initialize only if enabled - if (await service.IsEnabledAsync()) + if (await service.IsEnabledAsync().ConfigureAwait(false)) { - await service.TryInitializeAsync(); + await service.TryInitializeAsync().ConfigureAwait(false); // Register the extension for usage testSessionLifetimeHandlers.Add((service, _factoryOrdering.IndexOf(testSessionLifetimeHandlerFactory))); @@ -273,16 +270,12 @@ public void AddTestSessionLifetimeHandle(CompositeExtensionFactory composi var instance = (IExtension)compositeFactoryInstance.GetInstance(serviceProvider); // Check if we have already extensions of the same type with same id registered - if (testSessionLifetimeHandlers.Any(x => x.TestSessionLifetimeHandler.Uid == instance.Uid)) - { - (IExtension testSessionLifetimeHandler, int _) = testSessionLifetimeHandlers.Single(x => x.TestSessionLifetimeHandler.Uid == instance.Uid); - throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, PlatformResources.ExtensionWithSameUidAlreadyRegisteredErrorMessage, instance.Uid, testSessionLifetimeHandler.GetType())); - } + testSessionLifetimeHandlers.ValidateUniqueExtension(instance, x => x.TestSessionLifetimeHandler); // We initialize only if enabled - if (await instance.IsEnabledAsync()) + if (await instance.IsEnabledAsync().ConfigureAwait(false)) { - await instance.TryInitializeAsync(); + await instance.TryInitializeAsync().ConfigureAwait(false); } // Add to the list of shared singletons @@ -293,7 +286,7 @@ public void AddTestSessionLifetimeHandle(CompositeExtensionFactory composi var extension = (IExtension)compositeFactoryInstance.GetInstance(); // We register the extension only if enabled - if (await extension.IsEnabledAsync()) + if (await extension.IsEnabledAsync().ConfigureAwait(false)) { if (extension is ITestSessionLifetimeHandler testSessionLifetimeHandler) { @@ -307,6 +300,6 @@ public void AddTestSessionLifetimeHandle(CompositeExtensionFactory composi } } - return testSessionLifetimeHandlers.ToArray(); + return [.. testSessionLifetimeHandlers]; } } diff --git a/src/Platform/Microsoft.Testing.Platform/TestHostControllers/PassiveNodeDataConsumer.cs b/src/Platform/Microsoft.Testing.Platform/TestHostControllers/PassiveNodeDataConsumer.cs index 9e4c2e5be8..31001ace26 100644 --- a/src/Platform/Microsoft.Testing.Platform/TestHostControllers/PassiveNodeDataConsumer.cs +++ b/src/Platform/Microsoft.Testing.Platform/TestHostControllers/PassiveNodeDataConsumer.cs @@ -46,14 +46,14 @@ public async Task ConsumeAsync(IDataProducer dataProducer, IData value, Cancella case SessionFileArtifact sessionFileArtifact: { RunTestAttachment runTestAttachment = new(sessionFileArtifact.FileInfo.FullName, dataProducer.Uid, FileType, sessionFileArtifact.DisplayName, sessionFileArtifact.Description); - await _passiveNode.SendAttachmentsAsync(new TestsAttachments([runTestAttachment]), cancellationToken); + await _passiveNode.SendAttachmentsAsync(new TestsAttachments([runTestAttachment]), cancellationToken).ConfigureAwait(false); break; } case FileArtifact fileArtifact: { RunTestAttachment runTestAttachment = new(fileArtifact.FileInfo.FullName, dataProducer.Uid, FileType, fileArtifact.DisplayName, fileArtifact.Description); - await _passiveNode.SendAttachmentsAsync(new TestsAttachments([runTestAttachment]), cancellationToken); + await _passiveNode.SendAttachmentsAsync(new TestsAttachments([runTestAttachment]), cancellationToken).ConfigureAwait(false); break; } diff --git a/src/Platform/Microsoft.Testing.Platform/TestHostControllers/SystemEnvironmentVariableProvider.cs b/src/Platform/Microsoft.Testing.Platform/TestHostControllers/SystemEnvironmentVariableProvider.cs index 6e4e6bac6e..00bed2b5a6 100644 --- a/src/Platform/Microsoft.Testing.Platform/TestHostControllers/SystemEnvironmentVariableProvider.cs +++ b/src/Platform/Microsoft.Testing.Platform/TestHostControllers/SystemEnvironmentVariableProvider.cs @@ -20,7 +20,7 @@ internal sealed class SystemEnvironmentVariableProvider(IEnvironment environment public string Description => _systemExtension.Description; - public async Task IsEnabledAsync() => await _systemExtension.IsEnabledAsync(); + public async Task IsEnabledAsync() => await _systemExtension.IsEnabledAsync().ConfigureAwait(false); public Task UpdateAsync(IEnvironmentVariables environmentVariables) { diff --git a/src/Platform/Microsoft.Testing.Platform/TestHostControllers/TestHostControllersManager.cs b/src/Platform/Microsoft.Testing.Platform/TestHostControllers/TestHostControllersManager.cs index f04985b9db..cc0b74e43e 100644 --- a/src/Platform/Microsoft.Testing.Platform/TestHostControllers/TestHostControllersManager.cs +++ b/src/Platform/Microsoft.Testing.Platform/TestHostControllers/TestHostControllersManager.cs @@ -92,16 +92,12 @@ internal async Task BuildAsync(ServiceProvider ITestHostEnvironmentVariableProvider envVarProvider = environmentVariableProviderFactory(serviceProvider); // Check if we have already extensions of the same type with same id registered - if (environmentVariableProviders.Any(x => x.TestHostEnvironmentVariableProvider.Uid == envVarProvider.Uid)) - { - (ITestHostEnvironmentVariableProvider testHostEnvironmentVariableProvider, int _) = environmentVariableProviders.Single(x => x.TestHostEnvironmentVariableProvider.Uid == envVarProvider.Uid); - throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, PlatformResources.ExtensionWithSameUidAlreadyRegisteredErrorMessage, envVarProvider.Uid, testHostEnvironmentVariableProvider.GetType())); - } + environmentVariableProviders.ValidateUniqueExtension(envVarProvider, x => x.TestHostEnvironmentVariableProvider); // We initialize only if enabled - if (await envVarProvider.IsEnabledAsync()) + if (await envVarProvider.IsEnabledAsync().ConfigureAwait(false)) { - await envVarProvider.TryInitializeAsync(); + await envVarProvider.TryInitializeAsync().ConfigureAwait(false); // Register the extension for usage environmentVariableProviders.Add((envVarProvider, _factoryOrdering.IndexOf(environmentVariableProviderFactory))); @@ -113,22 +109,18 @@ internal async Task BuildAsync(ServiceProvider { // Get the singleton var extension = (IExtension)compositeServiceFactory.GetInstance(serviceProvider); - bool isEnabledAsync = await extension.IsEnabledAsync(); + bool isEnabledAsync = await extension.IsEnabledAsync().ConfigureAwait(false); // Check if we have already built the singleton for this composite factory if (!_alreadyBuiltServices.Contains(compositeServiceFactory)) { // Check if we have already extensions of the same type with same id registered - if (environmentVariableProviders.Any(x => x.TestHostEnvironmentVariableProvider.Uid == extension.Uid)) - { - (ITestHostEnvironmentVariableProvider testHostEnvironmentVariableProvider, int _) = environmentVariableProviders.Single(x => x.TestHostEnvironmentVariableProvider.Uid == extension.Uid); - throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, PlatformResources.ExtensionWithSameUidAlreadyRegisteredErrorMessage, extension.Uid, testHostEnvironmentVariableProvider.GetType())); - } + environmentVariableProviders.ValidateUniqueExtension(extension, x => x.TestHostEnvironmentVariableProvider); // We initialize only if enabled if (isEnabledAsync) { - await extension.TryInitializeAsync(); + await extension.TryInitializeAsync().ConfigureAwait(false); } // Add to the list of shared singletons @@ -157,16 +149,12 @@ internal async Task BuildAsync(ServiceProvider ITestHostProcessLifetimeHandler lifetimeHandler = lifetimeHandlerFactory(serviceProvider); // Check if we have already extensions of the same type with same id registered - if (lifetimeHandlers.Any(x => x.TestHostProcessLifetimeHandler.Uid == lifetimeHandler.Uid)) - { - (ITestHostProcessLifetimeHandler testHostProcessLifetimeHandler, int _) = lifetimeHandlers.Single(x => x.TestHostProcessLifetimeHandler.Uid == lifetimeHandler.Uid); - throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, PlatformResources.ExtensionWithSameUidAlreadyRegisteredErrorMessage, lifetimeHandler.Uid, testHostProcessLifetimeHandler.GetType())); - } + lifetimeHandlers.ValidateUniqueExtension(lifetimeHandler, x => x.TestHostProcessLifetimeHandler); // We initialize only if enabled - if (await lifetimeHandler.IsEnabledAsync()) + if (await lifetimeHandler.IsEnabledAsync().ConfigureAwait(false)) { - await lifetimeHandler.TryInitializeAsync(); + await lifetimeHandler.TryInitializeAsync().ConfigureAwait(false); // Register the extension for usage lifetimeHandlers.Add((lifetimeHandler, _factoryOrdering.IndexOf(lifetimeHandlerFactory))); @@ -178,21 +166,17 @@ internal async Task BuildAsync(ServiceProvider { // Get the singleton var extension = (IExtension)compositeServiceFactory.GetInstance(serviceProvider); - bool isEnabledAsync = await extension.IsEnabledAsync(); + bool isEnabledAsync = await extension.IsEnabledAsync().ConfigureAwait(false); // Check if we have already built the singleton for this composite factory if (!_alreadyBuiltServices.Contains(compositeServiceFactory)) { - if (lifetimeHandlers.Any(x => x.TestHostProcessLifetimeHandler.Uid == extension.Uid)) - { - (ITestHostProcessLifetimeHandler testHostProcessLifetimeHandler, int _) = lifetimeHandlers.Single(x => x.TestHostProcessLifetimeHandler.Uid == extension.Uid); - throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, PlatformResources.ExtensionWithSameUidAlreadyRegisteredErrorMessage, extension.Uid, testHostProcessLifetimeHandler.GetType())); - } + lifetimeHandlers.ValidateUniqueExtension(extension, x => x.TestHostProcessLifetimeHandler); // We initialize only if enabled if (isEnabledAsync) { - await extension.TryInitializeAsync(); + await extension.TryInitializeAsync().ConfigureAwait(false); } // Add to the list of shared singletons @@ -221,16 +205,12 @@ internal async Task BuildAsync(ServiceProvider IDataConsumer service = dataConsumerFactory(serviceProvider); // Check if we have already extensions of the same type with same id registered - if (dataConsumers.Any(x => x.Consumer.Uid == service.Uid)) - { - (IExtension consumer, int order) = dataConsumers.Single(x => x.Consumer.Uid == service.Uid); - throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, PlatformResources.ExtensionWithSameUidAlreadyRegisteredErrorMessage, service.Uid, consumer.GetType())); - } + dataConsumers.ValidateUniqueExtension(service, x => x.Consumer); // We initialize only if enabled - if (await service.IsEnabledAsync()) + if (await service.IsEnabledAsync().ConfigureAwait(false)) { - await service.TryInitializeAsync(); + await service.TryInitializeAsync().ConfigureAwait(false); // Register the extension for usage dataConsumers.Add((service, _factoryOrdering.IndexOf(dataConsumerFactory))); @@ -251,16 +231,12 @@ internal async Task BuildAsync(ServiceProvider var instance = (IExtension)compositeFactoryInstance.GetInstance(serviceProvider); // Check if we have already extensions of the same type with same id registered - if (dataConsumers.Any(x => x.Consumer.Uid == instance.Uid)) - { - (IExtension consumer, int _) = dataConsumers.Single(x => x.Consumer.Uid == instance.Uid); - throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, PlatformResources.ExtensionWithSameUidAlreadyRegisteredErrorMessage, instance.Uid, consumer.GetType())); - } + dataConsumers.ValidateUniqueExtension(instance, x => x.Consumer); // We initialize only if enabled - if (await instance.IsEnabledAsync()) + if (await instance.IsEnabledAsync().ConfigureAwait(false)) { - await instance.TryInitializeAsync(); + await instance.TryInitializeAsync().ConfigureAwait(false); } // Add to the list of shared singletons @@ -271,7 +247,7 @@ internal async Task BuildAsync(ServiceProvider var extension = (IExtension)compositeFactoryInstance.GetInstance(); // We register the extension only if enabled - if (await extension.IsEnabledAsync()) + if (await extension.IsEnabledAsync().ConfigureAwait(false)) { if (extension is IDataConsumer consumer) { @@ -286,9 +262,9 @@ internal async Task BuildAsync(ServiceProvider bool requireProcessRestart = environmentVariableProviders.Count > 0 || lifetimeHandlers.Count > 0 || dataConsumers.Count > 0; return new TestHostControllerConfiguration( - environmentVariableProviders.OrderBy(x => x.RegistrationOrder).Select(x => x.TestHostEnvironmentVariableProvider).ToArray(), - lifetimeHandlers.OrderBy(x => x.RegistrationOrder).Select(x => x.TestHostProcessLifetimeHandler).ToArray(), - dataConsumers.OrderBy(x => x.RegistrationOrder).Select(x => x.Consumer).ToArray(), + [.. environmentVariableProviders.OrderBy(x => x.RegistrationOrder).Select(x => x.TestHostEnvironmentVariableProvider)], + [.. lifetimeHandlers.OrderBy(x => x.RegistrationOrder).Select(x => x.TestHostProcessLifetimeHandler)], + [.. dataConsumers.OrderBy(x => x.RegistrationOrder).Select(x => x.Consumer)], requireProcessRestart); } } diff --git a/src/Platform/Microsoft.Testing.Platform/TestHostOrcherstrator/ITestHostControllersExtension.cs b/src/Platform/Microsoft.Testing.Platform/TestHostOrcherstrator/ITestHostControllersExtension.cs new file mode 100644 index 0000000000..02c81a5fee --- /dev/null +++ b/src/Platform/Microsoft.Testing.Platform/TestHostOrcherstrator/ITestHostControllersExtension.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.Platform.Extensions.TestHostOrchestrator; + +/// +/// Represents an extension for test host orchestrators. +/// +internal interface ITestHostOrchestratorExtension : IExtension; diff --git a/src/Platform/Microsoft.Testing.Platform/TestHostOrcherstrator/ITestHostOrchestratorApplicationLifetime.cs b/src/Platform/Microsoft.Testing.Platform/TestHostOrcherstrator/ITestHostOrchestratorApplicationLifetime.cs new file mode 100644 index 0000000000..93cb179eb8 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Platform/TestHostOrcherstrator/ITestHostOrchestratorApplicationLifetime.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 Microsoft.Testing.Platform.Extensions.TestHostOrchestrator; + +// NOTE: The equivalent of this for "test host" is ITestApplicationLifecycleCallbacks, which is an unfortunate naming. +// If we ever open orchestration before MTP v2 (https://github.com/microsoft/testfx/issues/5733), we should +// consider if we are okay with this kinda inconsistent naming between test host and test host orchestrator. +internal interface ITestHostOrchestratorApplicationLifetime : ITestHostOrchestratorExtension +{ + /// + /// Executes before the orchestrator runs. + /// + /// The cancellation token. + /// A task representing the asynchronous operation. + Task BeforeRunAsync(CancellationToken cancellationToken); + + /// + /// Executes after the orchestrator runs. + /// + /// The exit code of the orchestrator. + /// The cancellation token. + /// A task representing the asynchronous operation. + Task AfterRunAsync(int exitCode, CancellationToken cancellation); +} diff --git a/src/Platform/Microsoft.Testing.Platform/TestHostOrcherstrator/ITestHostOrchestratorManager.cs b/src/Platform/Microsoft.Testing.Platform/TestHostOrcherstrator/ITestHostOrchestratorManager.cs index 3ad59955fb..7f4d5c0827 100644 --- a/src/Platform/Microsoft.Testing.Platform/TestHostOrcherstrator/ITestHostOrchestratorManager.cs +++ b/src/Platform/Microsoft.Testing.Platform/TestHostOrcherstrator/ITestHostOrchestratorManager.cs @@ -9,5 +9,10 @@ internal interface ITestHostOrchestratorManager { void AddTestHostOrchestrator(Func factory); + // NOTE: In ITestHostManager, we have AddTestApplicationLifecycleCallbacks, which is an unfortunate naming. + // If we ever open orchestration before MTP v2 (https://github.com/microsoft/testfx/issues/5733), we should + // consider if we are okay with this kinda inconsistent naming between test host and test host orchestrator. + void AddTestHostOrchestratorApplicationLifetime(Func testHostOrchestratorApplicationLifetimeFactory); + Task BuildAsync(ServiceProvider serviceProvider); } diff --git a/src/Platform/Microsoft.Testing.Platform/TestHostOrcherstrator/TestHostOrchestratorManager.cs b/src/Platform/Microsoft.Testing.Platform/TestHostOrcherstrator/TestHostOrchestratorManager.cs index d6f7a628bf..e7678fba7e 100644 --- a/src/Platform/Microsoft.Testing.Platform/TestHostOrcherstrator/TestHostOrchestratorManager.cs +++ b/src/Platform/Microsoft.Testing.Platform/TestHostOrcherstrator/TestHostOrchestratorManager.cs @@ -1,13 +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. -using Microsoft.Testing.Platform.Resources; +using Microsoft.Testing.Platform.Helpers; using Microsoft.Testing.Platform.Services; namespace Microsoft.Testing.Platform.Extensions.TestHostOrchestrator; internal sealed class TestHostOrchestratorManager : ITestHostOrchestratorManager { + private readonly List> _testHostOrchestratorApplicationLifetimeFactories = []; private List>? _factories; public void AddTestHostOrchestrator(Func factory) @@ -30,22 +31,47 @@ public async Task BuildAsync(ServiceProvider ITestHostOrchestrator orchestrator = factory(serviceProvider); // Check if we have already extensions of the same type with same id registered - if (orchestrators.Any(x => x.Uid == orchestrator.Uid)) + orchestrators.ValidateUniqueExtension(orchestrator); + + // We initialize only if enabled + if (await orchestrator.IsEnabledAsync().ConfigureAwait(false)) { - ITestHostOrchestrator currentRegisteredExtension = orchestrators.Single(x => x.Uid == orchestrator.Uid); - throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, PlatformResources.ExtensionWithSameUidAlreadyRegisteredErrorMessage, orchestrator.Uid, currentRegisteredExtension.GetType())); + await orchestrator.TryInitializeAsync().ConfigureAwait(false); + + // Register the extension for usage + orchestrators.Add(orchestrator); } + } + + return new TestHostOrchestratorConfiguration([.. orchestrators]); + } + + public void AddTestHostOrchestratorApplicationLifetime(Func testHostOrchestratorApplicationLifetimeFactory) + { + Guard.NotNull(testHostOrchestratorApplicationLifetimeFactory); + _testHostOrchestratorApplicationLifetimeFactories.Add(testHostOrchestratorApplicationLifetimeFactory); + } + + internal async Task BuildTestHostOrchestratorApplicationLifetimesAsync(ServiceProvider serviceProvider) + { + List lifetimes = []; + foreach (Func testHostOrchestratorApplicationLifetimeFactory in _testHostOrchestratorApplicationLifetimeFactories) + { + ITestHostOrchestratorApplicationLifetime service = testHostOrchestratorApplicationLifetimeFactory(serviceProvider); + + // Check if we have already extensions of the same type with same id registered + lifetimes.ValidateUniqueExtension(service); // We initialize only if enabled - if (await orchestrator.IsEnabledAsync()) + if (await service.IsEnabledAsync().ConfigureAwait(false)) { - await orchestrator.TryInitializeAsync(); + await service.TryInitializeAsync().ConfigureAwait(false); // Register the extension for usage - orchestrators.Add(orchestrator); + lifetimes.Add(service); } } - return new TestHostOrchestratorConfiguration(orchestrators.ToArray()); + return [.. lifetimes]; } } diff --git a/src/Platform/Microsoft.Testing.Platform/Tools/ToolsManager.cs b/src/Platform/Microsoft.Testing.Platform/Tools/ToolsManager.cs index 869c60d440..69f2b595cf 100644 --- a/src/Platform/Microsoft.Testing.Platform/Tools/ToolsManager.cs +++ b/src/Platform/Microsoft.Testing.Platform/Tools/ToolsManager.cs @@ -21,12 +21,12 @@ internal async Task> BuildAsync(IServiceProvider servicePro foreach (Func toolFactory in _toolsFactories) { ITool tool = toolFactory(serviceProvider); - if (!await tool.IsEnabledAsync()) + if (!await tool.IsEnabledAsync().ConfigureAwait(false)) { continue; } - await tool.TryInitializeAsync(); + await tool.TryInitializeAsync().ConfigureAwait(false); tools.Add(tool); } diff --git a/src/TestFramework/TestFramework.Extensions/Attributes/UWP_UITestMethodAttribute.cs b/src/TestFramework/TestFramework.Extensions/Attributes/UWP_UITestMethodAttribute.cs index 6be0c1acea..59e96e8f1c 100644 --- a/src/TestFramework/TestFramework.Extensions/Attributes/UWP_UITestMethodAttribute.cs +++ b/src/TestFramework/TestFramework.Extensions/Attributes/UWP_UITestMethodAttribute.cs @@ -10,7 +10,7 @@ namespace Microsoft.VisualStudio.TestTools.UnitTesting.AppContainer; /// public class UITestMethodAttribute : TestMethodAttribute { - private protected override bool UseAsync => true; + private protected override bool UseAsync => GetType() == typeof(UITestMethodAttribute); /// public override TestResult[] Execute(ITestMethod testMethod) => base.Execute(testMethod); @@ -34,7 +34,7 @@ await Windows.ApplicationModel.Core.CoreApplication.MainView.CoreWindow.Dispatch { try { - tcs.SetResult(await testMethod.InvokeAsync(null)); + tcs.SetResult(await testMethod.InvokeAsync(null).ConfigureAwait(false)); } catch (Exception ex) { @@ -43,7 +43,7 @@ await Windows.ApplicationModel.Core.CoreApplication.MainView.CoreWindow.Dispatch }); #pragma warning restore VSTHRD101 // Avoid unsupported async delegates - return [await tcs.Task]; + return [await tcs.Task.ConfigureAwait(false)]; } } #endif diff --git a/src/TestFramework/TestFramework.Extensions/Attributes/WinUITestTargetAttribute.cs b/src/TestFramework/TestFramework.Extensions/Attributes/WinUITestTargetAttribute.cs index e440b2f31d..d3cd4dd81c 100644 --- a/src/TestFramework/TestFramework.Extensions/Attributes/WinUITestTargetAttribute.cs +++ b/src/TestFramework/TestFramework.Extensions/Attributes/WinUITestTargetAttribute.cs @@ -23,7 +23,7 @@ public WinUITestTargetAttribute(Type applicationType) if (!typeof(UI.Xaml.Application).IsAssignableFrom(applicationType)) { - throw new ArgumentException(string.Format(CultureInfo.InvariantCulture, FrameworkMessages.ArgumentXMustDeriveFromClassY, nameof(applicationType), "Microsoft.UI.Xaml.Application"), nameof(applicationType)); + throw new ArgumentException(string.Format(CultureInfo.InvariantCulture, FrameworkExtensionsMessages.ArgumentXMustDeriveFromClassY, nameof(applicationType), "Microsoft.UI.Xaml.Application"), nameof(applicationType)); } ApplicationType = applicationType; diff --git a/src/TestFramework/TestFramework.Extensions/Attributes/WinUI_UITestMethodAttribute.cs b/src/TestFramework/TestFramework.Extensions/Attributes/WinUI_UITestMethodAttribute.cs index 37f81c56dc..e059339d3a 100644 --- a/src/TestFramework/TestFramework.Extensions/Attributes/WinUI_UITestMethodAttribute.cs +++ b/src/TestFramework/TestFramework.Extensions/Attributes/WinUI_UITestMethodAttribute.cs @@ -34,7 +34,7 @@ public UITestMethodAttribute(string displayName) { } - private protected override bool UseAsync => true; + private protected override bool UseAsync => GetType() == typeof(UITestMethodAttribute); /// /// Gets or sets the that should be used to invoke the UITestMethodAttribute. @@ -62,12 +62,12 @@ internal override async Task ExecuteAsync(ITestMethod testMethod) { // TODO: Code seems to be assuming DeclaringType is never null, but it can be null. // Using 'bang' notation for now to ensure same behavior. - DispatcherQueue dispatcher = GetDispatcherQueue(testMethod.MethodInfo.DeclaringType!.Assembly) ?? throw new InvalidOperationException(FrameworkMessages.AsyncUITestMethodWithNoDispatcherQueue); + DispatcherQueue dispatcher = GetDispatcherQueue(testMethod.MethodInfo.DeclaringType!.Assembly) ?? throw new InvalidOperationException(FrameworkExtensionsMessages.AsyncUITestMethodWithNoDispatcherQueue); if (dispatcher.HasThreadAccess) { try { - return [await testMethod.InvokeAsync(null)]; + return [await testMethod.InvokeAsync(null).ConfigureAwait(false)]; } catch (Exception e) { @@ -82,7 +82,7 @@ internal override async Task ExecuteAsync(ITestMethod testMethod) { try { - tcs.SetResult(await testMethod.InvokeAsync(null)); + tcs.SetResult(await testMethod.InvokeAsync(null).ConfigureAwait(false)); } catch (Exception e) { @@ -94,7 +94,7 @@ internal override async Task ExecuteAsync(ITestMethod testMethod) } #pragma warning restore VSTHRD101 // Avoid unsupported async delegates - return [await tcs.Task]; + return [await tcs.Task.ConfigureAwait(false)]; } private static Type? GetApplicationType(Assembly assembly) diff --git a/src/TestFramework/TestFramework.Extensions/Friends.cs b/src/TestFramework/TestFramework.Extensions/Friends.cs deleted file mode 100644 index 57566ec3fd..0000000000 --- a/src/TestFramework/TestFramework.Extensions/Friends.cs +++ /dev/null @@ -1,10 +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. - -// Friend assemblies -[assembly: InternalsVisibleTo("Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices, PublicKey=002400000480000094000000060200000024000052534131000400000100010007d1fa57c4aed9f0a32e84aa0faefd0de9e8fd6aec8f87fb03766c834c99921eb23be79ad9d5dcc1dd9ad236132102900b723cf980957fc4e177108fc607774f29e8320e92ea05ece4e821c0a5efe8f1645c4c0c93c1ab99285d622caa652c1dfad63d745d6f2de5f17e5eaf0fc4963d261c8a12436518206dc093344d5ad293")] -[assembly: InternalsVisibleTo("MSTestAdapter.PlatformServices.UnitTests, PublicKey=002400000480000094000000060200000024000052534131000400000100010007d1fa57c4aed9f0a32e84aa0faefd0de9e8fd6aec8f87fb03766c834c99921eb23be79ad9d5dcc1dd9ad236132102900b723cf980957fc4e177108fc607774f29e8320e92ea05ece4e821c0a5efe8f1645c4c0c93c1ab99285d622caa652c1dfad63d745d6f2de5f17e5eaf0fc4963d261c8a12436518206dc093344d5ad293")] - -#if WINDOWS_UWP || WIN_UI -[assembly: InternalsVisibleTo("Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices, PublicKey=002400000480000094000000060200000024000052534131000400000100010007d1fa57c4aed9f0a32e84aa0faefd0de9e8fd6aec8f87fb03766c834c99921eb23be79ad9d5dcc1dd9ad236132102900b723cf980957fc4e177108fc607774f29e8320e92ea05ece4e821c0a5efe8f1645c4c0c93c1ab99285d622caa652c1dfad63d745d6f2de5f17e5eaf0fc4963d261c8a12436518206dc093344d5ad293")] -#endif diff --git a/src/TestFramework/TestFramework.Extensions/MSTest.TestFramework.NonWindows.nuspec b/src/TestFramework/TestFramework.Extensions/MSTest.TestFramework.NonWindows.nuspec index af5c322364..d214368c74 100644 --- a/src/TestFramework/TestFramework.Extensions/MSTest.TestFramework.NonWindows.nuspec +++ b/src/TestFramework/TestFramework.Extensions/MSTest.TestFramework.NonWindows.nuspec @@ -34,21 +34,25 @@ $CommonFileElements$ + + + + - + @@ -57,7 +61,7 @@ - + @@ -66,7 +70,7 @@ - + @@ -75,7 +79,7 @@ - + diff --git a/src/TestFramework/TestFramework.Extensions/MSTest.TestFramework.nuspec b/src/TestFramework/TestFramework.Extensions/MSTest.TestFramework.nuspec index 67a4e010a2..deeb4715bd 100644 --- a/src/TestFramework/TestFramework.Extensions/MSTest.TestFramework.nuspec +++ b/src/TestFramework/TestFramework.Extensions/MSTest.TestFramework.nuspec @@ -47,35 +47,43 @@ $CommonFileElements$ + + + + + + + + - + @@ -86,7 +94,7 @@ - + @@ -95,7 +103,7 @@ - + @@ -104,7 +112,7 @@ - + diff --git a/src/TestFramework/TestFramework.Extensions/MessageLevel.cs b/src/TestFramework/TestFramework.Extensions/MessageLevel.cs index 064b617131..4d6fecd6f7 100644 --- a/src/TestFramework/TestFramework.Extensions/MessageLevel.cs +++ b/src/TestFramework/TestFramework.Extensions/MessageLevel.cs @@ -4,7 +4,7 @@ namespace Microsoft.VisualStudio.TestTools.UnitTesting; /// -/// Specifies the severity level of messages displayed using the API. +/// Specifies the severity level of messages displayed using the API. /// public enum MessageLevel { diff --git a/src/TestFramework/TestFramework.Extensions/PublicAPI/PublicAPI.Shipped.txt b/src/TestFramework/TestFramework.Extensions/PublicAPI/PublicAPI.Shipped.txt index 1e3ad80187..b50cde008f 100644 --- a/src/TestFramework/TestFramework.Extensions/PublicAPI/PublicAPI.Shipped.txt +++ b/src/TestFramework/TestFramework.Extensions/PublicAPI/PublicAPI.Shipped.txt @@ -18,6 +18,7 @@ Microsoft.VisualStudio.TestTools.UnitTesting.TestContext.TestDisplayName.get -> Microsoft.VisualStudio.TestTools.UnitTesting.TestContext.TestDisplayName.set -> void Microsoft.VisualStudio.TestTools.UnitTesting.TestContext.TestException.get -> System.Exception? Microsoft.VisualStudio.TestTools.UnitTesting.TestContext.TestException.set -> void +Microsoft.VisualStudio.TestTools.UnitTesting.TestContext.TestRunCount.get -> int virtual Microsoft.VisualStudio.TestTools.UnitTesting.TestContext.CancellationTokenSource.get -> System.Threading.CancellationTokenSource! virtual Microsoft.VisualStudio.TestTools.UnitTesting.TestContext.CancellationTokenSource.set -> void virtual Microsoft.VisualStudio.TestTools.UnitTesting.TestContext.CurrentTestOutcome.get -> Microsoft.VisualStudio.TestTools.UnitTesting.UnitTestOutcome diff --git a/src/TestFramework/TestFramework.Extensions/PublicAPI/PublicAPI.Unshipped.txt b/src/TestFramework/TestFramework.Extensions/PublicAPI/PublicAPI.Unshipped.txt index 3fbc1ee8a4..7505aa4cf1 100644 --- a/src/TestFramework/TestFramework.Extensions/PublicAPI/PublicAPI.Unshipped.txt +++ b/src/TestFramework/TestFramework.Extensions/PublicAPI/PublicAPI.Unshipped.txt @@ -1,2 +1,2 @@ #nullable enable -Microsoft.VisualStudio.TestTools.UnitTesting.TestContext.TestRunCount.get -> int +Microsoft.VisualStudio.TestTools.UnitTesting.TestContext.CancellationToken.get -> System.Threading.CancellationToken diff --git a/src/TestFramework/TestFramework.Extensions/Resources/FrameworkExtensionsMessages.resx b/src/TestFramework/TestFramework.Extensions/Resources/FrameworkExtensionsMessages.resx new file mode 100644 index 0000000000..7e55257e77 --- /dev/null +++ b/src/TestFramework/TestFramework.Extensions/Resources/FrameworkExtensionsMessages.resx @@ -0,0 +1,130 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 + + + Type '{0}' is not assignable to '{1}'. + + - {0} argument name like "applicationType" + - {1} fully qualified class name like "Microsoft.UI.Xaml.Application" + + + + UITestMethodAttribute.DispatcherQueue should not be null. To use UITestMethodAttribute within a WinUI Desktop App, remember to set the static UITestMethodAttribute.DispatcherQueue during the test initialization. + + \ No newline at end of file diff --git a/src/TestFramework/TestFramework.Extensions/Resources/xlf/FrameworkExtensionsMessages.cs.xlf b/src/TestFramework/TestFramework.Extensions/Resources/xlf/FrameworkExtensionsMessages.cs.xlf new file mode 100644 index 0000000000..df112b7c30 --- /dev/null +++ b/src/TestFramework/TestFramework.Extensions/Resources/xlf/FrameworkExtensionsMessages.cs.xlf @@ -0,0 +1,20 @@ + + + + + + Type '{0}' is not assignable to '{1}'. + Typ „{0}“ nelze přiřadit k „{1}“. + + - {0} argument name like "applicationType" + - {1} fully qualified class name like "Microsoft.UI.Xaml.Application" + + + + UITestMethodAttribute.DispatcherQueue should not be null. To use UITestMethodAttribute within a WinUI Desktop App, remember to set the static UITestMethodAttribute.DispatcherQueue during the test initialization. + UITestMethodAttribute.DispatcherQueue nesmí mít hodnotu null. Pokud chcete použít UITestMethodAttribute v desktopové aplikaci WinUI, nezapomeňte během inicializace testu nastavit statický UITestMethodAttribute.DispatcherQueue. + + + + + \ No newline at end of file diff --git a/src/TestFramework/TestFramework.Extensions/Resources/xlf/FrameworkExtensionsMessages.de.xlf b/src/TestFramework/TestFramework.Extensions/Resources/xlf/FrameworkExtensionsMessages.de.xlf new file mode 100644 index 0000000000..a764dda7b3 --- /dev/null +++ b/src/TestFramework/TestFramework.Extensions/Resources/xlf/FrameworkExtensionsMessages.de.xlf @@ -0,0 +1,20 @@ + + + + + + Type '{0}' is not assignable to '{1}'. + Der Typ "{0}" kann nicht "{1}" zugewiesen werden. + + - {0} argument name like "applicationType" + - {1} fully qualified class name like "Microsoft.UI.Xaml.Application" + + + + UITestMethodAttribute.DispatcherQueue should not be null. To use UITestMethodAttribute within a WinUI Desktop App, remember to set the static UITestMethodAttribute.DispatcherQueue during the test initialization. + UITestMethodAttribute.DispatcherQueue darf nicht NULL sein. Um UITestMethodAttribute in einer WinUI-Desktop-App zu verwenden, denken Sie daran, die statische UITestMethodAttribute.DispatcherQueue während der Testinitialisierung festzulegen. + + + + + \ No newline at end of file diff --git a/src/TestFramework/TestFramework.Extensions/Resources/xlf/FrameworkExtensionsMessages.es.xlf b/src/TestFramework/TestFramework.Extensions/Resources/xlf/FrameworkExtensionsMessages.es.xlf new file mode 100644 index 0000000000..17912d03f1 --- /dev/null +++ b/src/TestFramework/TestFramework.Extensions/Resources/xlf/FrameworkExtensionsMessages.es.xlf @@ -0,0 +1,20 @@ + + + + + + Type '{0}' is not assignable to '{1}'. + Tipo "{0}" no se puede asignar a "{1}". + + - {0} argument name like "applicationType" + - {1} fully qualified class name like "Microsoft.UI.Xaml.Application" + + + + UITestMethodAttribute.DispatcherQueue should not be null. To use UITestMethodAttribute within a WinUI Desktop App, remember to set the static UITestMethodAttribute.DispatcherQueue during the test initialization. + UITestMethodAttribute.DispatcherQueue no debe ser null. Para usar UITestMethodAttribute en una aplicación de escritorio WinUI, recuerde establecer el UITestMethodAttribute.DispatcherQueue estático durante la inicialización de la prueba. + + + + + \ No newline at end of file diff --git a/src/TestFramework/TestFramework.Extensions/Resources/xlf/FrameworkExtensionsMessages.fr.xlf b/src/TestFramework/TestFramework.Extensions/Resources/xlf/FrameworkExtensionsMessages.fr.xlf new file mode 100644 index 0000000000..6a4b6f969e --- /dev/null +++ b/src/TestFramework/TestFramework.Extensions/Resources/xlf/FrameworkExtensionsMessages.fr.xlf @@ -0,0 +1,20 @@ + + + + + + Type '{0}' is not assignable to '{1}'. + Impossible d'assigner le type '{0}' à '{1}'. + + - {0} argument name like "applicationType" + - {1} fully qualified class name like "Microsoft.UI.Xaml.Application" + + + + UITestMethodAttribute.DispatcherQueue should not be null. To use UITestMethodAttribute within a WinUI Desktop App, remember to set the static UITestMethodAttribute.DispatcherQueue during the test initialization. + UITestMethodAttribute.DispatcherQueue ne doit pas avoir la valeur nul. Pour utiliser UITestMethodAttribute dans une application de bureau WinUI, n’oubliez pas de définir l’UITestMethodAttribute.DispatcherQueue statique pendant l’initialisation du test. + + + + + \ No newline at end of file diff --git a/src/TestFramework/TestFramework.Extensions/Resources/xlf/FrameworkExtensionsMessages.it.xlf b/src/TestFramework/TestFramework.Extensions/Resources/xlf/FrameworkExtensionsMessages.it.xlf new file mode 100644 index 0000000000..5cd8d9568a --- /dev/null +++ b/src/TestFramework/TestFramework.Extensions/Resources/xlf/FrameworkExtensionsMessages.it.xlf @@ -0,0 +1,20 @@ + + + + + + Type '{0}' is not assignable to '{1}'. + Il tipo '{0}' non è assegnabile a '{1}'. + + - {0} argument name like "applicationType" + - {1} fully qualified class name like "Microsoft.UI.Xaml.Application" + + + + UITestMethodAttribute.DispatcherQueue should not be null. To use UITestMethodAttribute within a WinUI Desktop App, remember to set the static UITestMethodAttribute.DispatcherQueue during the test initialization. + UITestMethodAttribute.DispatcherQueue non deve essere Null. Per usare UITestMethodAttribute all'interno di un'app desktop WinUI, ricordarsi di impostare il parametro statico uiTestMethodAttribute.DispatcherQueue durante l'inizializzazione del test. + + + + + \ No newline at end of file diff --git a/src/TestFramework/TestFramework.Extensions/Resources/xlf/FrameworkExtensionsMessages.ja.xlf b/src/TestFramework/TestFramework.Extensions/Resources/xlf/FrameworkExtensionsMessages.ja.xlf new file mode 100644 index 0000000000..4d3d045657 --- /dev/null +++ b/src/TestFramework/TestFramework.Extensions/Resources/xlf/FrameworkExtensionsMessages.ja.xlf @@ -0,0 +1,20 @@ + + + + + + Type '{0}' is not assignable to '{1}'. + 型 '{0}' を '{1}' に割り当てることはできません。 + + - {0} argument name like "applicationType" + - {1} fully qualified class name like "Microsoft.UI.Xaml.Application" + + + + UITestMethodAttribute.DispatcherQueue should not be null. To use UITestMethodAttribute within a WinUI Desktop App, remember to set the static UITestMethodAttribute.DispatcherQueue during the test initialization. + UITestMethodAttribute.DispatcherQueue を null にすることはできません。WinUI デスクトップ アプリ内で UITestMethodAttribute を使用するには、テストの初期化中に静的な UITestMethodAttribute.DispatcherQueue を設定してください。 + + + + + \ No newline at end of file diff --git a/src/TestFramework/TestFramework.Extensions/Resources/xlf/FrameworkExtensionsMessages.ko.xlf b/src/TestFramework/TestFramework.Extensions/Resources/xlf/FrameworkExtensionsMessages.ko.xlf new file mode 100644 index 0000000000..7749a50ad7 --- /dev/null +++ b/src/TestFramework/TestFramework.Extensions/Resources/xlf/FrameworkExtensionsMessages.ko.xlf @@ -0,0 +1,20 @@ + + + + + + Type '{0}' is not assignable to '{1}'. + '{0}' 형식은 '{1}'에 할당할 수 없습니다. + + - {0} argument name like "applicationType" + - {1} fully qualified class name like "Microsoft.UI.Xaml.Application" + + + + UITestMethodAttribute.DispatcherQueue should not be null. To use UITestMethodAttribute within a WinUI Desktop App, remember to set the static UITestMethodAttribute.DispatcherQueue during the test initialization. + UITestMethodAttribute.DispatcherQueue는 null이 아니어야 합니다. WinUI 데스크톱 앱 내에서 UITestMethodAttribute를 사용하려면 테스트 초기화 중에 정적 UITestMethodAttribute.DispatcherQueue를 설정해야 합니다. + + + + + \ No newline at end of file diff --git a/src/TestFramework/TestFramework.Extensions/Resources/xlf/FrameworkExtensionsMessages.pl.xlf b/src/TestFramework/TestFramework.Extensions/Resources/xlf/FrameworkExtensionsMessages.pl.xlf new file mode 100644 index 0000000000..cacee965b7 --- /dev/null +++ b/src/TestFramework/TestFramework.Extensions/Resources/xlf/FrameworkExtensionsMessages.pl.xlf @@ -0,0 +1,20 @@ + + + + + + Type '{0}' is not assignable to '{1}'. + Typu „{0}” nie można przypisać do typu „{1}”. + + - {0} argument name like "applicationType" + - {1} fully qualified class name like "Microsoft.UI.Xaml.Application" + + + + UITestMethodAttribute.DispatcherQueue should not be null. To use UITestMethodAttribute within a WinUI Desktop App, remember to set the static UITestMethodAttribute.DispatcherQueue during the test initialization. + Element UITestMethodAttribute.DispatcherQueue nie powinien mieć wartości null. Aby użyć atrybutu UITestMethodAttribute w aplikacji klasycznej WinUI, pamiętaj o ustawieniu statycznego atrybutu UITestMethodAttribute.DispatcherQueue podczas inicjowania testu. + + + + + \ No newline at end of file diff --git a/src/TestFramework/TestFramework.Extensions/Resources/xlf/FrameworkExtensionsMessages.pt-BR.xlf b/src/TestFramework/TestFramework.Extensions/Resources/xlf/FrameworkExtensionsMessages.pt-BR.xlf new file mode 100644 index 0000000000..abd65e40f5 --- /dev/null +++ b/src/TestFramework/TestFramework.Extensions/Resources/xlf/FrameworkExtensionsMessages.pt-BR.xlf @@ -0,0 +1,20 @@ + + + + + + Type '{0}' is not assignable to '{1}'. + Tipo '{0}' não é atribuível a '{1}'. + + - {0} argument name like "applicationType" + - {1} fully qualified class name like "Microsoft.UI.Xaml.Application" + + + + UITestMethodAttribute.DispatcherQueue should not be null. To use UITestMethodAttribute within a WinUI Desktop App, remember to set the static UITestMethodAttribute.DispatcherQueue during the test initialization. + UITestMethodAttribute.DispatcherQueue não deve ser nulo. Para usar UITestMethodAttribute em um aplicativo WinUI Desktop, lembre-se de definir o UITestMethodAttribute.DispatcherQueue estático durante a inicialização do teste. + + + + + \ No newline at end of file diff --git a/src/TestFramework/TestFramework.Extensions/Resources/xlf/FrameworkExtensionsMessages.ru.xlf b/src/TestFramework/TestFramework.Extensions/Resources/xlf/FrameworkExtensionsMessages.ru.xlf new file mode 100644 index 0000000000..36e3e0934c --- /dev/null +++ b/src/TestFramework/TestFramework.Extensions/Resources/xlf/FrameworkExtensionsMessages.ru.xlf @@ -0,0 +1,20 @@ + + + + + + Type '{0}' is not assignable to '{1}'. + Тип "{0}" не может быть назначен "{1}". + + - {0} argument name like "applicationType" + - {1} fully qualified class name like "Microsoft.UI.Xaml.Application" + + + + UITestMethodAttribute.DispatcherQueue should not be null. To use UITestMethodAttribute within a WinUI Desktop App, remember to set the static UITestMethodAttribute.DispatcherQueue during the test initialization. + Параметр UITestMethodAttribute.DispatcherQueue не должен иметь значение NULL. Чтобы использовать параметр UITestMethodAttribute в классических приложениях WinUI, не забудьте задать статический параметр UITestMethodAttribute.DispatcherQueue во время инициализации теста. + + + + + \ No newline at end of file diff --git a/src/TestFramework/TestFramework.Extensions/Resources/xlf/FrameworkExtensionsMessages.tr.xlf b/src/TestFramework/TestFramework.Extensions/Resources/xlf/FrameworkExtensionsMessages.tr.xlf new file mode 100644 index 0000000000..60c31e4c21 --- /dev/null +++ b/src/TestFramework/TestFramework.Extensions/Resources/xlf/FrameworkExtensionsMessages.tr.xlf @@ -0,0 +1,20 @@ + + + + + + Type '{0}' is not assignable to '{1}'. + '{0}' tipi '{1}'ye atanamaz. + + - {0} argument name like "applicationType" + - {1} fully qualified class name like "Microsoft.UI.Xaml.Application" + + + + UITestMethodAttribute.DispatcherQueue should not be null. To use UITestMethodAttribute within a WinUI Desktop App, remember to set the static UITestMethodAttribute.DispatcherQueue during the test initialization. + UITestMethodAttribute.DispatcherQueue boş olmamalıdır. UITestMethodAttribute'ı bir WinUI Masaüstü Uygulamasında kullanmak için, test başlatma sırasında statik UITestMethodAttribute.DispatcherQueue'yu ayarlamayı unutmayın. + + + + + \ No newline at end of file diff --git a/src/TestFramework/TestFramework.Extensions/Resources/xlf/FrameworkExtensionsMessages.zh-Hans.xlf b/src/TestFramework/TestFramework.Extensions/Resources/xlf/FrameworkExtensionsMessages.zh-Hans.xlf new file mode 100644 index 0000000000..0d2a247a4d --- /dev/null +++ b/src/TestFramework/TestFramework.Extensions/Resources/xlf/FrameworkExtensionsMessages.zh-Hans.xlf @@ -0,0 +1,20 @@ + + + + + + Type '{0}' is not assignable to '{1}'. + 类型“{0}”不能分配给“{1}”。 + + - {0} argument name like "applicationType" + - {1} fully qualified class name like "Microsoft.UI.Xaml.Application" + + + + UITestMethodAttribute.DispatcherQueue should not be null. To use UITestMethodAttribute within a WinUI Desktop App, remember to set the static UITestMethodAttribute.DispatcherQueue during the test initialization. + UITestMethodAttribute.DispatcherQueue 不应为 null。若要在 WinUI 桌面应用中使用 UITestMethodAttribute,请在测试初始化期间设置静态 UITestMethodAttribute.DispatcherQueue。 + + + + + \ No newline at end of file diff --git a/src/TestFramework/TestFramework.Extensions/Resources/xlf/FrameworkExtensionsMessages.zh-Hant.xlf b/src/TestFramework/TestFramework.Extensions/Resources/xlf/FrameworkExtensionsMessages.zh-Hant.xlf new file mode 100644 index 0000000000..90f4e3bb61 --- /dev/null +++ b/src/TestFramework/TestFramework.Extensions/Resources/xlf/FrameworkExtensionsMessages.zh-Hant.xlf @@ -0,0 +1,20 @@ + + + + + + Type '{0}' is not assignable to '{1}'. + 無法將類型 '{0}' {0} 指派給 '{1}。 + + - {0} argument name like "applicationType" + - {1} fully qualified class name like "Microsoft.UI.Xaml.Application" + + + + UITestMethodAttribute.DispatcherQueue should not be null. To use UITestMethodAttribute within a WinUI Desktop App, remember to set the static UITestMethodAttribute.DispatcherQueue during the test initialization. + UITestMethodAttribute.DispatcherQueue 不應為 Null。若要在 WinUI 傳統型應用程式內使用 UITestMethodAttribute,請記得在測試初始化期間設定靜態 UITestMethodAttribute.DispatcherQueue。 + + + + + \ No newline at end of file diff --git a/src/TestFramework/TestFramework.Extensions/TestContext.cs b/src/TestFramework/TestFramework.Extensions/TestContext.cs index ccbb8a68ea..8031c2f87d 100644 --- a/src/TestFramework/TestFramework.Extensions/TestContext.cs +++ b/src/TestFramework/TestFramework.Extensions/TestContext.cs @@ -6,6 +6,8 @@ using System.Data.Common; #endif +using System.ComponentModel; + namespace Microsoft.VisualStudio.TestTools.UnitTesting; /// @@ -48,8 +50,15 @@ public abstract class TestContext /// /// Gets or sets the cancellation token source. This token source is canceled when test times out. Also when explicitly canceled the test will be aborted. /// + // Disposing isn't important per https://github.com/dotnet/runtime/issues/29970#issuecomment-717840778 + [EditorBrowsable(EditorBrowsableState.Never)] public virtual CancellationTokenSource CancellationTokenSource { get; protected internal set; } = new(); + /// + /// Gets the cancellation token. This token is canceled when test times out. Also when explicitly canceled the test will be aborted. + /// + public CancellationToken CancellationToken => CancellationTokenSource.Token; + /// /// Gets or sets the test data for the test method being executed. /// diff --git a/src/TestFramework/TestFramework.Extensions/TestFramework.Extensions.csproj b/src/TestFramework/TestFramework.Extensions/TestFramework.Extensions.csproj index 134b272c10..67cdf269a5 100644 --- a/src/TestFramework/TestFramework.Extensions/TestFramework.Extensions.csproj +++ b/src/TestFramework/TestFramework.Extensions/TestFramework.Extensions.csproj @@ -36,7 +36,7 @@ Microsoft.VisualStudio.TestTools.UnitTesting Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions - TRACE + $(DefineConstants);TRACE @@ -88,16 +88,13 @@ + - - - - - PreserveNewest - + + diff --git a/src/TestFramework/TestFramework.Extensions/build/net462/MSTest.TestFramework.targets b/src/TestFramework/TestFramework.Extensions/build/net462/MSTest.TestFramework.targets new file mode 100644 index 0000000000..f80fd5bb22 --- /dev/null +++ b/src/TestFramework/TestFramework.Extensions/build/net462/MSTest.TestFramework.targets @@ -0,0 +1,3 @@ + + + diff --git a/src/TestFramework/TestFramework.Extensions/build/netcoreapp3.1/MSTest.TestFramework.targets b/src/TestFramework/TestFramework.Extensions/build/netcoreapp3.1/MSTest.TestFramework.targets new file mode 100644 index 0000000000..a9e20a1c40 --- /dev/null +++ b/src/TestFramework/TestFramework.Extensions/build/netcoreapp3.1/MSTest.TestFramework.targets @@ -0,0 +1,3 @@ + + + diff --git a/src/TestFramework/TestFramework.Extensions/build/netstandard2.0/MSTest.TestFramework.targets b/src/TestFramework/TestFramework.Extensions/build/netstandard2.0/MSTest.TestFramework.targets new file mode 100644 index 0000000000..d4afeaee4a --- /dev/null +++ b/src/TestFramework/TestFramework.Extensions/build/netstandard2.0/MSTest.TestFramework.targets @@ -0,0 +1,3 @@ + + + diff --git a/src/TestFramework/TestFramework.Extensions/build/uap10.0/MSTest.TestFramework.targets b/src/TestFramework/TestFramework.Extensions/build/uap10.0/MSTest.TestFramework.targets new file mode 100644 index 0000000000..9b248fe99e --- /dev/null +++ b/src/TestFramework/TestFramework.Extensions/build/uap10.0/MSTest.TestFramework.targets @@ -0,0 +1,3 @@ + + + diff --git a/src/TestFramework/TestFramework.Extensions/buildTransitive/winui+uwp/MSTest.TestFramework.targets b/src/TestFramework/TestFramework.Extensions/buildTransitive/net6.0AndLater/MSTest.TestFramework.targets similarity index 68% rename from src/TestFramework/TestFramework.Extensions/buildTransitive/winui+uwp/MSTest.TestFramework.targets rename to src/TestFramework/TestFramework.Extensions/buildTransitive/net6.0AndLater/MSTest.TestFramework.targets index 4fa711c2c7..5d2ec50262 100644 --- a/src/TestFramework/TestFramework.Extensions/buildTransitive/winui+uwp/MSTest.TestFramework.targets +++ b/src/TestFramework/TestFramework.Extensions/buildTransitive/net6.0AndLater/MSTest.TestFramework.targets @@ -20,4 +20,12 @@ + + + + diff --git a/src/TestFramework/TestFramework.Extensions/buildTransitive/others/MSTest.TestFramework.targets b/src/TestFramework/TestFramework.Extensions/buildTransitive/others/MSTest.TestFramework.targets new file mode 100644 index 0000000000..7ab2dd1dd4 --- /dev/null +++ b/src/TestFramework/TestFramework.Extensions/buildTransitive/others/MSTest.TestFramework.targets @@ -0,0 +1,11 @@ + + + + + + + diff --git a/src/TestFramework/TestFramework/Assertions/Assert.AreEqual.cs b/src/TestFramework/TestFramework/Assertions/Assert.AreEqual.cs index 930ddb4504..6bb911e170 100644 --- a/src/TestFramework/TestFramework/Assertions/Assert.AreEqual.cs +++ b/src/TestFramework/TestFramework/Assertions/Assert.AreEqual.cs @@ -694,6 +694,104 @@ private static bool AreEqualFailing(decimal expected, decimal actual, decimal de private static bool AreEqualFailing(long expected, long actual, long delta) => Math.Abs(expected - actual) > delta; + private static string FormatStringComparisonMessage(string? expected, string? actual, string userMessage) + { + // Handle null cases + if (expected is null && actual is null) + { + return string.Format( + CultureInfo.CurrentCulture, + FrameworkMessages.AreEqualFailMsg, + userMessage, + ReplaceNulls(expected), + ReplaceNulls(actual)); + } + + if (expected is null || actual is null) + { + return string.Format( + CultureInfo.CurrentCulture, + FrameworkMessages.AreEqualFailMsg, + userMessage, + ReplaceNulls(expected), + ReplaceNulls(actual)); + } + + // Find the first difference + int diffIndex = FindFirstStringDifference(expected, actual); + + if (diffIndex == -1) + { + // Strings are equal - should not happen in practice, we call this method only when they are not equal. + throw ApplicationStateGuard.Unreachable(); + } + + // Format the enhanced string comparison message + return FormatStringDifferenceMessage(expected, actual, diffIndex, userMessage); + } + + private static int FindFirstStringDifference(string expected, string actual) + { + int minLength = Math.Min(expected.Length, actual.Length); + + for (int i = 0; i < minLength; i++) + { + if (expected[i] != actual[i]) + { + return i; + } + } + + // If we reach here, one string is a prefix of the other + return expected.Length != actual.Length ? minLength : -1; + } + + private static string FormatStringDifferenceMessage(string expected, string actual, int diffIndex, string userMessage) + { + string lengthInfo = expected.Length == actual.Length + ? string.Format(CultureInfo.CurrentCulture, FrameworkMessages.AreEqualStringDiffLengthBothMsg, expected.Length, diffIndex) + : string.Format(CultureInfo.CurrentCulture, FrameworkMessages.AreEqualStringDiffLengthDifferentMsg, expected.Length, actual.Length); + + // Create contextual preview around the difference + const int contextLength = 41; // Show up to 20 characters of context on each side + Tuple tuple = StringPreviewHelper.CreateStringPreviews(expected, actual, diffIndex, contextLength); + string expectedPreview = tuple.Item1; + string actualPreview = tuple.Item2; + int caretPosition = tuple.Item3; + + // Get localized prefixes + string expectedPrefix = FrameworkMessages.AreEqualStringDiffExpectedPrefix; + string actualPrefix = FrameworkMessages.AreEqualStringDiffActualPrefix; + + // Calculate the maximum prefix length to align the caret properly + int maxPrefixLength = Math.Max(expectedPrefix.Length, actualPrefix.Length); + + // Pad shorter prefix to match the longer one for proper alignment + string paddedExpectedPrefix = expectedPrefix.PadRight(maxPrefixLength); + string paddedActualPrefix = actualPrefix.PadRight(maxPrefixLength); + + // Build the formatted lines with proper alignment + string expectedLine = paddedExpectedPrefix + $"\"{expectedPreview}\""; + string actualLine = paddedActualPrefix + $"\"{actualPreview}\""; + + // The caret should align under the difference in the string content + // For localized prefixes with different lengths, we need to account for the longer prefix + // to ensure proper alignment. But the caret position is relative to the string content. + int adjustedCaretPosition = maxPrefixLength + 1 + caretPosition; // +1 for the opening quote + + // Format user message properly - add leading space if not empty, otherwise no extra formatting + string formattedUserMessage = string.IsNullOrEmpty(userMessage) ? string.Empty : $" {userMessage}"; + + return string.Format( + CultureInfo.CurrentCulture, + FrameworkMessages.AreEqualStringDiffFailMsg, + lengthInfo, + formattedUserMessage, + expectedLine, + actualLine, + new string('-', adjustedCaretPosition) + "^"); + } + [DoesNotReturn] private static void ThrowAssertAreEqualFailed(object? expected, object? actual, string userMessage) { @@ -706,12 +804,14 @@ private static void ThrowAssertAreEqualFailed(object? expected, object? actual, expected.GetType().FullName, ReplaceNulls(actual), actual.GetType().FullName) - : string.Format( - CultureInfo.CurrentCulture, - FrameworkMessages.AreEqualFailMsg, - userMessage, - ReplaceNulls(expected), - ReplaceNulls(actual)); + : expected is string expectedString && actual is string actualString + ? FormatStringComparisonMessage(expectedString, actualString, userMessage) + : string.Format( + CultureInfo.CurrentCulture, + FrameworkMessages.AreEqualFailMsg, + userMessage, + ReplaceNulls(expected), + ReplaceNulls(actual)); ThrowAssertFailed("Assert.AreEqual", finalMessage); } @@ -732,20 +832,23 @@ private static void ThrowAssertAreEqualFailed(T expected, T actual, T delta, [DoesNotReturn] private static void ThrowAssertAreEqualFailed(string? expected, string? actual, bool ignoreCase, CultureInfo culture, string userMessage) { + string finalMessage; + // If the user requested to match case, and the difference between expected/actual is casing only, then we use a different message. - string finalMessage = !ignoreCase && CompareInternal(expected, actual, ignoreCase: true, culture) == 0 - ? string.Format( + if (!ignoreCase && CompareInternal(expected, actual, ignoreCase: true, culture) == 0) + { + finalMessage = string.Format( CultureInfo.CurrentCulture, FrameworkMessages.AreEqualCaseFailMsg, userMessage, ReplaceNulls(expected), - ReplaceNulls(actual)) - : string.Format( - CultureInfo.CurrentCulture, - FrameworkMessages.AreEqualFailMsg, - userMessage, - ReplaceNulls(expected), ReplaceNulls(actual)); + } + else + { + // Use enhanced string comparison for string-specific failures + finalMessage = FormatStringComparisonMessage(expected, actual, userMessage); + } ThrowAssertFailed("Assert.AreEqual", finalMessage); } @@ -2118,3 +2221,119 @@ private static void ThrowAssertAreNotEqualFailed(object? notExpected, object? ac ThrowAssertFailed("Assert.AreNotEqual", finalMessage); } } + +internal static class StringPreviewHelper +{ + public static Tuple CreateStringPreviews(string expected, string actual, int diffIndex, int fullPreviewLength) + { + int ellipsisLength = 3; // Length of the ellipsis "..." + + if (fullPreviewLength % 2 == 0) + { + // Being odd makes it easier to calculate the context length, and center the marker, this is not user customizable. + throw new ArgumentException($"{nameof(fullPreviewLength)} must be odd, but it was even."); + } + + // This is arbitrary number that is 2 times the size of the ellipsis, + // plus 3 chars to make it easier to check the tests are correct when part of string is masked. + // Preview length is not user customizable, just makes it harder to break the tests, and avoids few ifs we would need to write otherwise. + if (fullPreviewLength < 9) + { + throw new ArgumentException($"{nameof(fullPreviewLength)} cannot be shorter than 9."); + } + + // In case we want to instead count runes or text elements we can change it just here. + int expectedLength = expected.Length; + int actualLength = actual.Length; + + if (diffIndex < 0 || diffIndex > Math.Min(expectedLength, actualLength)) // Not -1 here because the difference can be right after the end of the shorter string. + { + throw new ArgumentOutOfRangeException(nameof(diffIndex), "diffIndex must be within the bounds of both strings."); + } + + int contextLength = (fullPreviewLength - 1) / 2; + + // Diff index must point into the string, the start of the strings will always be shortened the same amount, + // because otherwise the diff would happen at the beginning of the string. + // So we just care about how far we are from the end of the string, so we can show the maximum amount of info to the user + // when diff is really close to the end. + string shorterString = expectedLength < actualLength ? expected : actual; + string longerString = expectedLength < actualLength ? actual : expected; + bool expectedIsShorter = expectedLength < actualLength; + + int shorterStringLength = shorterString.Length; + int longerStringLength = longerString.Length; + + // End marker will point to the end of the shorter string, but the end of the longer string will be replaced by ... + // make sure we don't point at the dots. To do this we need to make sure the strings are cut at the beginning, rather than preferring the maximum context shown. + bool markerPointsAtEllipsis = longerStringLength - shorterStringLength > ellipsisLength && shorterStringLength - diffIndex < ellipsisLength; + int ellipsisSpaceOrZero = markerPointsAtEllipsis ? ellipsisLength + 2 : 0; + + // Find the end of the string that we will show, either then end of the shorter string, or the end of the preview window. + // Then calculate the start of the preview from that. This makes sure that if diff is close end of the string we show as much as we can. + int start = Math.Min(diffIndex + contextLength, shorterStringLength) - fullPreviewLength + ellipsisSpaceOrZero; + + // If the string is shorter than the preview, start cutting from 0, otherwise start cutting from the calculated start. + int cutStart = Math.Max(0, start); + // From here we need to handle longer and shorter string separately, because one of the can be shorter, + // and we want to show the maximum we can that fits in thew preview window. + int cutEndShort = Math.Min(cutStart + fullPreviewLength, shorterStringLength); + int cutEndLong = Math.Min(cutStart + fullPreviewLength, longerStringLength); + + string shorterStringPreview = shorterString.Substring(cutStart, cutEndShort - cutStart); + string longerStringPreview = longerString.Substring(cutStart, cutEndLong - cutStart); + + // We cut something from the start of the string, so we need to add ellipsis there. + // We know if one string is cut then both must be cut, otherwise the diff would be at the beginning of the string. + if (cutStart > 0) + { + shorterStringPreview = EllipsisStart(shorterStringPreview); + longerStringPreview = EllipsisStart(longerStringPreview); + } + + // We cut something from the end of the string, so we need to add ellipsis there. + // We don't know if both strings are cut, so we need to check them separately. + if (cutEndShort < shorterStringLength) + { + shorterStringPreview = EllipsisEnd(shorterStringPreview); + } + + // We cut something from the end of the string, so we need to add ellipsis there. + if (cutEndLong < longerStringLength) + { + longerStringPreview = EllipsisEnd(longerStringPreview); + } + + string escapedShorterStringPreview = MakeControlCharactersVisible(shorterStringPreview); + string escapedLongerStringPreview = MakeControlCharactersVisible(longerStringPreview); + + return new Tuple( + expectedIsShorter ? escapedShorterStringPreview : escapedLongerStringPreview, + expectedIsShorter ? escapedLongerStringPreview : escapedShorterStringPreview, + diffIndex - cutStart); + } + + private static string EllipsisEnd(string text) + => $"{text.Substring(0, text.Length - 3)}..."; + + private static string EllipsisStart(string text) + => $"...{text.Substring(3)}"; + + private static string MakeControlCharactersVisible(string text) + { + var stringBuilder = new StringBuilder(text.Length); + foreach (char ch in text) + { + if (char.IsControl(ch)) + { + stringBuilder.Append((char)(0x2400 + ch)); + } + else + { + stringBuilder.Append(ch); + } + } + + return stringBuilder.ToString(); + } +} diff --git a/src/TestFramework/TestFramework/Assertions/Assert.AreSame.cs b/src/TestFramework/TestFramework/Assertions/Assert.AreSame.cs index 5a80a9b523..61d0a6700d 100644 --- a/src/TestFramework/TestFramework/Assertions/Assert.AreSame.cs +++ b/src/TestFramework/TestFramework/Assertions/Assert.AreSame.cs @@ -220,7 +220,7 @@ public static void AreSame(T? expected, T? actual, [StringSyntax(StringSyntax } private static bool IsAreSameFailing(T? expected, T? actual) - => !ReferenceEquals(expected, actual); + => !object.ReferenceEquals(expected, actual); [DoesNotReturn] private static void ThrowAssertAreSameFailed(T? expected, T? actual, string userMessage) @@ -325,7 +325,7 @@ public static void AreNotSame(T? notExpected, T? actual, [StringSyntax(String } private static bool IsAreNotSameFailing(T? notExpected, T? actual) - => ReferenceEquals(notExpected, actual); + => object.ReferenceEquals(notExpected, actual); [DoesNotReturn] private static void ThrowAssertAreNotSameFailed(string userMessage) diff --git a/src/TestFramework/TestFramework/Assertions/Assert.Contains.cs b/src/TestFramework/TestFramework/Assertions/Assert.Contains.cs index 74abfeb3cd..80eb6ffcf3 100644 --- a/src/TestFramework/TestFramework/Assertions/Assert.Contains.cs +++ b/src/TestFramework/TestFramework/Assertions/Assert.Contains.cs @@ -5,6 +5,8 @@ namespace Microsoft.VisualStudio.TestTools.UnitTesting; +#pragma warning disable RS0027 // API with optional parameter(s) should have the most parameters amongst its public overloads + /// /// A collection of helper classes to test various conditions within /// unit tests. If the condition being tested is not met, an exception @@ -35,11 +37,11 @@ public AssertSingleInterpolatedStringHandler(int literalLength, int formattedCou } } - internal TItem ComputeAssertion(string assertionName) + internal TItem ComputeAssertion() { if (_builder is not null) { - ThrowAssertCountFailed(assertionName, 1, _actualCount, _builder.ToString()); + ThrowAssertContainsSingleFailed(_actualCount, _builder.ToString()); } return _item!; @@ -55,10 +57,8 @@ public void AppendFormatted(T value) public void AppendFormatted(ReadOnlySpan value) => _builder!.Append(value); -#pragma warning disable RS0027 // API with optional parameter(s) should have the most parameters amongst its public overloads public void AppendFormatted(ReadOnlySpan value, int alignment = 0, string? format = null) => AppendFormatted(value.ToString(), alignment, format); -#pragma warning restore RS0027 // API with optional parameter(s) should have the most parameters amongst its public overloads #endif // NOTE: All the overloads involving format and/or alignment are not super efficient. @@ -79,14 +79,12 @@ public void AppendFormatted(string? value) => _builder!.Append(value); #pragma warning disable RS0026 // Do not add multiple public overloads with optional parameters -#pragma warning disable RS0027 // API with optional parameter(s) should have the most parameters amongst its public overloads public void AppendFormatted(string? value, int alignment = 0, string? format = null) => _builder!.AppendFormat(null, $"{{0,{alignment}:{format}}}", value); public void AppendFormatted(object? value, int alignment = 0, string? format = null) => _builder!.AppendFormat(null, $"{{0,{alignment}:{format}}}", value); #pragma warning restore RS0026 // Do not add multiple public overloads with optional parameters -#pragma warning restore RS0027 // API with optional parameter(s) should have the most parameters amongst its public overloads } #pragma warning restore CS1591 // Missing XML comment for publicly visible type or member @@ -119,7 +117,7 @@ public static T ContainsSingle(IEnumerable collection, string? message) #pragma warning disable IDE0060 // Remove unused parameter public static T ContainsSingle(IEnumerable collection, [InterpolatedStringHandlerArgument(nameof(collection))] ref AssertSingleInterpolatedStringHandler message) #pragma warning restore IDE0060 // Remove unused parameter - => message.ComputeAssertion("ContainsSingle"); + => message.ComputeAssertion(); /// /// Tests whether the specified collection contains exactly one element. @@ -138,7 +136,32 @@ public static T ContainsSingle(IEnumerable collection, [StringSyntax(Strin } string userMessage = BuildUserMessage(message, parameters); - ThrowAssertCountFailed("ContainsSingle", 1, actualCount, userMessage); + ThrowAssertContainsSingleFailed(actualCount, userMessage); + + // Unreachable code but compiler cannot work it out + return default; + } + + /// + /// Tests whether the specified collection contains exactly one element that matches the given predicate. + /// + /// The type of the collection items. + /// A function to test each element for a condition. + /// The collection. + /// The message format to display when the assertion fails. + /// The item that matches the predicate. + public static T ContainsSingle(Func predicate, IEnumerable collection, string message = "") + { + var matchingElements = collection.Where(predicate).ToList(); + int actualCount = matchingElements.Count; + + if (actualCount == 1) + { + return matchingElements[0]; + } + + string userMessage = BuildUserMessage(message); + ThrowAssertSingleMatchFailed(actualCount, userMessage); // Unreachable code but compiler cannot work it out return default; @@ -155,6 +178,14 @@ public static T ContainsSingle(IEnumerable collection, [StringSyntax(Strin public static void Contains(T expected, IEnumerable collection) => Contains(expected, collection, string.Empty, null); + /// + /// Tests whether the specified non-generic collection contains the given element. + /// + /// The expected item. + /// The non-generic collection (like ArrayList). + public static void Contains(object? expected, IEnumerable collection) + => Contains(expected, collection, string.Empty); + /// /// Tests whether the specified collection contains the given element. /// @@ -178,10 +209,30 @@ public static void Contains(T expected, IEnumerable collection, string? me if (!collection.Contains(expected)) { string userMessage = BuildUserMessage(message, parameters); - ThrowAssertFailed("Contains", userMessage); + ThrowAssertContainsItemFailed(userMessage); } } + /// + /// Tests whether the specified collection contains the given element. + /// + /// The expected item. + /// The collection. + /// The message format to display when the assertion fails. + public static void Contains(object? expected, IEnumerable collection, string? message) + { + foreach (object? item in collection) + { + if (object.Equals(item, expected)) + { + return; + } + } + + string userMessage = BuildUserMessage(message); + ThrowAssertContainsItemFailed(userMessage); + } + /// /// Tests whether the specified collection contains the given element. /// @@ -192,6 +243,15 @@ public static void Contains(T expected, IEnumerable collection, string? me public static void Contains(T expected, IEnumerable collection, IEqualityComparer comparer) => Contains(expected, collection, comparer, string.Empty, null); + /// + /// Tests whether the specified collection contains the given element. + /// + /// The expected item. + /// The collection. + /// An equality comparer to compare values. + public static void Contains(object expected, IEnumerable collection, IEqualityComparer comparer) + => Contains(expected, collection, comparer, string.Empty); + /// /// Tests whether the specified collection contains the given element. /// @@ -217,10 +277,31 @@ public static void Contains(T expected, IEnumerable collection, IEqualityC if (!collection.Contains(expected, comparer)) { string userMessage = BuildUserMessage(message, parameters); - ThrowAssertFailed("Contains", userMessage); + ThrowAssertContainsItemFailed(userMessage); } } + /// + /// Tests whether the specified collection contains the given element. + /// + /// The expected item. + /// The collection. + /// An equality comparer to compare values. + /// The message to display when the assertion fails. + public static void Contains(object expected, IEnumerable collection, IEqualityComparer comparer, string? message) + { + foreach (object? item in collection) + { + if (comparer.Equals(item, expected)) + { + return; + } + } + + string userMessage = BuildUserMessage(message); + ThrowAssertContainsItemFailed(userMessage); + } + /// /// Tests whether the specified collection contains the given element. /// @@ -233,11 +314,19 @@ public static void Contains(Func predicate, IEnumerable collectio /// /// Tests whether the specified collection contains the given element. /// - /// The type of the collection items. + /// A function to test each element for a condition. /// The collection. + public static void Contains(Func predicate, IEnumerable collection) + => Contains(predicate, collection, string.Empty); + + /// + /// Tests whether the specified collection contains the given element. + /// + /// The type of the collection items. /// A function to test each element for a condition. + /// The collection. /// The message to display when the assertion fails. - public static void Contains(IEnumerable collection, Func predicate, string? message) + public static void Contains(Func predicate, IEnumerable collection, string? message) => Contains(predicate, collection, message, null); /// @@ -253,8 +342,28 @@ public static void Contains(Func predicate, IEnumerable collectio if (!collection.Any(predicate)) { string userMessage = BuildUserMessage(message, parameters); - ThrowAssertFailed("Contains", userMessage); + ThrowAssertContainsPredicateFailed(userMessage); + } + } + + /// + /// Tests whether the specified collection contains the given element. + /// + /// A function to test each element for a condition. + /// The collection. + /// The message format to display when the assertion fails. + public static void Contains(Func predicate, IEnumerable collection, string? message) + { + foreach (object? item in collection) + { + if (predicate(item)) + { + return; + } } + + string userMessage = BuildUserMessage(message); + ThrowAssertContainsPredicateFailed(userMessage); } /// @@ -409,7 +518,7 @@ public static void Contains(string substring, string value, StringComparison com { string userMessage = BuildUserMessage(message, parameters); string finalMessage = string.Format(CultureInfo.CurrentCulture, FrameworkMessages.ContainsFail, value, substring, userMessage); - ThrowAssertFailed("StringAssert.Contains", finalMessage); + ThrowAssertFailed("Assert.Contains", finalMessage); } } @@ -426,6 +535,14 @@ public static void Contains(string substring, string value, StringComparison com public static void DoesNotContain(T expected, IEnumerable collection) => DoesNotContain(expected, collection, string.Empty, null); + /// + /// Tests whether the specified non-generic collection does not contain the specified item. + /// + /// The expected item. + /// The non-generic collection. + public static void DoesNotContain(object? expected, IEnumerable collection) + => DoesNotContain(expected, collection, string.Empty); + /// /// Tests whether the specified collection does not contain the specified item. /// @@ -449,7 +566,25 @@ public static void DoesNotContain(T expected, IEnumerable collection, stri if (collection.Contains(expected)) { string userMessage = BuildUserMessage(message, parameters); - ThrowAssertFailed("DoesNotContain", userMessage); + ThrowAssertDoesNotContainItemFailed(userMessage); + } + } + + /// + /// Tests whether the specified non-generic collection does not contain the specified item. + /// + /// The expected item. + /// The non-generic collection. + /// The message to display when the assertion fails. + public static void DoesNotContain(object? expected, IEnumerable collection, string? message) + { + foreach (object? item in collection) + { + if (object.Equals(expected, item)) + { + string userMessage = BuildUserMessage(message); + ThrowAssertDoesNotContainItemFailed(userMessage); + } } } @@ -463,6 +598,15 @@ public static void DoesNotContain(T expected, IEnumerable collection, stri public static void DoesNotContain(T expected, IEnumerable collection, IEqualityComparer comparer) => DoesNotContain(expected, collection, comparer, string.Empty, null); + /// + /// Tests whether the specified collection does not contain the specified item. + /// + /// The expected item. + /// The collection. + /// An equality comparer to compare values. + public static void DoesNotContain(object? expected, IEnumerable collection, IEqualityComparer comparer) + => DoesNotContain(expected, collection, comparer, string.Empty); + /// /// Tests whether the specified collection does not contain the specified item. /// @@ -488,7 +632,27 @@ public static void DoesNotContain(T expected, IEnumerable collection, IEqu if (collection.Contains(expected, comparer)) { string userMessage = BuildUserMessage(message, parameters); - ThrowAssertFailed("DoesNotContain", userMessage); + ThrowAssertDoesNotContainItemFailed(userMessage); + } + } + + /// + /// Tests whether the specified non-generic collection does not contain the specified item, + /// using a custom equality comparer. + /// + /// The expected item. + /// The non-generic collection. + /// An equality comparer to compare values. + /// The message to display when the assertion fails. + public static void DoesNotContain(object? expected, IEnumerable collection, IEqualityComparer comparer, string? message) + { + foreach (object? item in collection) + { + if (comparer.Equals(item, expected)) + { + string userMessage = BuildUserMessage(message); + ThrowAssertDoesNotContainItemFailed(userMessage); + } } } @@ -501,6 +665,14 @@ public static void DoesNotContain(T expected, IEnumerable collection, IEqu public static void DoesNotContain(Func predicate, IEnumerable collection) => DoesNotContain(predicate, collection, string.Empty, null); + /// + /// Tests whether the specified collection does not contain the specified item. + /// + /// A function to test each element for a condition. + /// The collection. + public static void DoesNotContain(Func predicate, IEnumerable collection) + => DoesNotContain(predicate, collection, string.Empty); + /// /// Tests whether the specified collection does not contain the specified item. /// @@ -524,65 +696,83 @@ public static void DoesNotContain(Func predicate, IEnumerable col if (collection.Any(predicate)) { string userMessage = BuildUserMessage(message, parameters); - ThrowAssertFailed("DoesNotContain", userMessage); + ThrowAssertDoesNotContainPredicateFailed(userMessage); } } /// - /// Tests whether the specified string contains the specified substring - /// and throws an exception if the substring does not occur within the + /// Tests whether the specified collection does not contain the specified item. + /// + /// A function to test each element for a condition. + /// The collection. + /// The message to display when the assertion fails. + public static void DoesNotContain(Func predicate, IEnumerable collection, string? message) + { + foreach (object? item in collection) + { + if (predicate(item)) + { + string userMessage = BuildUserMessage(message); + ThrowAssertDoesNotContainPredicateFailed(userMessage); + } + } + } + + /// + /// Tests whether the specified string does not contain the specified substring + /// and throws an exception if the substring occurs within the /// test string. /// /// - /// The string expected to occur within . + /// The string expected to not occur within . /// /// - /// The string that is expected to contain . + /// The string that is expected to not contain . /// /// /// is null, or is null, - /// or does not contain . + /// or contains . /// public static void DoesNotContain(string substring, string value) => DoesNotContain(substring, value, StringComparison.Ordinal, string.Empty); /// - /// Tests whether the specified string contains the specified substring - /// and throws an exception if the substring does not occur within the + /// Tests whether the specified string does not contain the specified substring + /// and throws an exception if the substring occurs within the /// test string. /// /// - /// The string expected to occur within . + /// The string expected to not occur within . /// /// - /// The string that is expected to contain . + /// The string that is expected to not contain . /// /// /// The message to include in the exception when - /// is not in . The message is shown in + /// is in . The message is shown in /// test results. /// /// /// is null, or is null, - /// or does not contain . + /// or contains . /// public static void DoesNotContain(string substring, string value, string? message) => DoesNotContain(substring, value, StringComparison.Ordinal, message); /// - /// Tests whether the specified string contains the specified substring - /// and throws an exception if the substring does not occur within the + /// Tests whether the specified string does not contain the specified substring + /// and throws an exception if the substring occurs within the /// test string. /// /// - /// The string expected to occur within . + /// The string expected to not occur within . /// /// - /// The string that is expected to contain . + /// The string that is expected to not contain . /// /// /// The message to include in the exception when - /// is not in . The message is shown in + /// is in . The message is shown in /// test results. /// /// @@ -590,76 +780,76 @@ public static void DoesNotContain(string substring, string value, string? messag /// /// /// is null, or is null, - /// or does not contain . + /// or contains . /// public static void DoesNotContain(string substring, string value, [StringSyntax(StringSyntaxAttribute.CompositeFormat)] string? message, params object?[]? parameters) => DoesNotContain(substring, value, StringComparison.Ordinal, message, parameters); /// - /// Tests whether the specified string contains the specified substring - /// and throws an exception if the substring does not occur within the + /// Tests whether the specified string does not contain the specified substring + /// and throws an exception if the substring occurs within the /// test string. /// /// - /// The string expected to occur within . + /// The string expected to not occur within . /// /// - /// The string that is expected to contain . + /// The string that is expected to not contain . /// /// /// The comparison method to compare strings . /// /// /// is null, or is null, - /// or does not contain . + /// or contains . /// public static void DoesNotContain(string substring, string value, StringComparison comparisonType) => DoesNotContain(substring, value, comparisonType, string.Empty); /// - /// Tests whether the specified string contains the specified substring - /// and throws an exception if the substring does not occur within the + /// Tests whether the specified string does not contain the specified substring + /// and throws an exception if the substring occurs within the /// test string. /// /// - /// The string expected to occur within . + /// The string expected to not occur within . /// /// - /// The string that is expected to contain . + /// The string that is expected to not contain . /// /// /// The comparison method to compare strings . /// /// /// The message to include in the exception when - /// is not in . The message is shown in + /// is in . The message is shown in /// test results. /// /// /// is null, or is null, - /// or does not contain . + /// or contains . /// public static void DoesNotContain(string substring, string value, StringComparison comparisonType, string? message) => DoesNotContain(substring, value, comparisonType, message, null); /// - /// Tests whether the specified string contains the specified substring - /// and throws an exception if the substring does not occur within the + /// Tests whether the specified string does not contain the specified substring + /// and throws an exception if the substring occurs within the /// test string. /// /// - /// The string expected to occur within . + /// The string expected to not occur within . /// /// - /// The string that is expected to contain . + /// The string that is expected to not contain . /// /// /// The comparison method to compare strings . /// /// /// The message to include in the exception when - /// is not in . The message is shown in + /// is in . The message is shown in /// test results. /// /// @@ -667,7 +857,7 @@ public static void DoesNotContain(string substring, string value, StringComparis /// /// /// is null, or is null, - /// or does not contain . + /// or contains . /// public static void DoesNotContain(string substring, string value, StringComparison comparisonType, [StringSyntax(StringSyntaxAttribute.CompositeFormat)] string? message, params object?[]? parameters) @@ -680,9 +870,100 @@ public static void DoesNotContain(string substring, string value, StringComparis { string userMessage = BuildUserMessage(message, parameters); string finalMessage = string.Format(CultureInfo.CurrentCulture, FrameworkMessages.DoesNotContainFail, value, substring, userMessage); - ThrowAssertFailed("StringAssert.DoesNotContain", finalMessage); + ThrowAssertFailed("Assert.DoesNotContain", finalMessage); } } #endregion // DoesNotContain + + #region IsInRange + + /// + /// Tests whether the specified value is within the expected range (inclusive). + /// The range includes both the minimum and maximum values. + /// + /// The type of the values to compare. + /// The minimum value of the expected range (inclusive). + /// The maximum value of the expected range (inclusive). + /// The value to test. + /// The message format to display when the assertion fails. + public static void IsInRange(T minValue, T maxValue, T value, string message = "") + where T : struct, IComparable + { + if (maxValue.CompareTo(minValue) <= 0) + { + throw new ArgumentOutOfRangeException(nameof(maxValue), "The maximum value must be greater than the minimum value."); + } + + if (value.CompareTo(minValue) < 0 || value.CompareTo(maxValue) > 0) + { + string userMessage = BuildUserMessage(message); + string finalMessage = string.Format(CultureInfo.CurrentCulture, FrameworkMessages.IsInRangeFail, value, minValue, maxValue, userMessage); + ThrowAssertFailed("IsInRange", finalMessage); + } + } + + #endregion // IsInRange + + [DoesNotReturn] + private static void ThrowAssertSingleMatchFailed(int actualCount, string userMessage) + { + string finalMessage = string.Format( + CultureInfo.CurrentCulture, + FrameworkMessages.ContainsSingleMatchFailMsg, + userMessage, + actualCount); + ThrowAssertFailed("Assert.ContainsSingle", finalMessage); + } + + [DoesNotReturn] + private static void ThrowAssertContainsSingleFailed(int actualCount, string userMessage) + { + string finalMessage = string.Format( + CultureInfo.CurrentCulture, + FrameworkMessages.ContainsSingleFailMsg, + userMessage, + actualCount); + ThrowAssertFailed("Assert.ContainsSingle", finalMessage); + } + + [DoesNotReturn] + private static void ThrowAssertContainsItemFailed(string userMessage) + { + string finalMessage = string.Format( + CultureInfo.CurrentCulture, + FrameworkMessages.ContainsItemFailMsg, + userMessage); + ThrowAssertFailed("Assert.Contains", finalMessage); + } + + [DoesNotReturn] + private static void ThrowAssertContainsPredicateFailed(string userMessage) + { + string finalMessage = string.Format( + CultureInfo.CurrentCulture, + FrameworkMessages.ContainsPredicateFailMsg, + userMessage); + ThrowAssertFailed("Assert.Contains", finalMessage); + } + + [DoesNotReturn] + private static void ThrowAssertDoesNotContainItemFailed(string userMessage) + { + string finalMessage = string.Format( + CultureInfo.CurrentCulture, + FrameworkMessages.DoesNotContainItemFailMsg, + userMessage); + ThrowAssertFailed("Assert.DoesNotContain", finalMessage); + } + + [DoesNotReturn] + private static void ThrowAssertDoesNotContainPredicateFailed(string userMessage) + { + string finalMessage = string.Format( + CultureInfo.CurrentCulture, + FrameworkMessages.DoesNotContainPredicateFailMsg, + userMessage); + ThrowAssertFailed("Assert.DoesNotContain", finalMessage); + } } diff --git a/src/TestFramework/TestFramework/Assertions/Assert.Count.cs b/src/TestFramework/TestFramework/Assertions/Assert.Count.cs index 5c01a9e767..0a9dba0f79 100644 --- a/src/TestFramework/TestFramework/Assertions/Assert.Count.cs +++ b/src/TestFramework/TestFramework/Assertions/Assert.Count.cs @@ -171,6 +171,13 @@ public void AppendFormatted(object? value, int alignment = 0, string? format = n public static void IsNotEmpty(IEnumerable collection) => IsNotEmpty(collection, string.Empty, null); + /// + /// Tests that the collection is not empty. + /// + /// The collection. + public static void IsNotEmpty(IEnumerable collection) + => IsNotEmpty(collection, string.Empty); + /// /// Tests whether the collection is not empty. /// @@ -209,6 +216,22 @@ public static void IsNotEmpty(IEnumerable collection, [StringSyntax(String ThrowAssertIsNotEmptyFailed(userMessage); } + /// + /// Tests that the collection is not empty. + /// + /// The collection. + /// The message format to display when the assertion fails. + public static void IsNotEmpty(IEnumerable collection, string? message) + { + if (collection.Cast().Any()) + { + return; + } + + string userMessage = BuildUserMessage(message); + ThrowAssertIsNotEmptyFailed(userMessage); + } + /// /// Tests whether the collection has the expected count/length. /// @@ -218,6 +241,14 @@ public static void IsNotEmpty(IEnumerable collection, [StringSyntax(String public static void HasCount(int expected, IEnumerable collection) => HasCount(expected, collection, string.Empty, null); + /// + /// Tests whether the collection has the expected count/length. + /// + /// The expected count. + /// The collection. + public static void HasCount(int expected, IEnumerable collection) + => HasCount(expected, collection, string.Empty); + /// /// Tests whether the collection has the expected count/length. /// @@ -251,6 +282,15 @@ public static void HasCount(int expected, IEnumerable collection, [Interpo public static void HasCount(int expected, IEnumerable collection, [StringSyntax(StringSyntaxAttribute.CompositeFormat)] string? message, params object?[]? parameters) => HasCount("HasCount", expected, collection, message, parameters); + /// + /// Tests whether the collection has the expected count/length. + /// + /// The expected count. + /// The collection. + /// The message format to display when the assertion fails. + public static void HasCount(int expected, IEnumerable collection, string? message) + => HasCount("HasCount", expected, collection, message); + /// /// Tests that the collection is empty. /// @@ -259,6 +299,13 @@ public static void HasCount(int expected, IEnumerable collection, [StringS public static void IsEmpty(IEnumerable collection) => IsEmpty(collection, string.Empty, null); + /// + /// Tests that the collection is empty. + /// + /// The collection. + public static void IsEmpty(IEnumerable collection) + => IsEmpty(collection, string.Empty); + /// /// Tests that the collection is empty. /// @@ -289,6 +336,14 @@ public static void IsEmpty(IEnumerable collection, [InterpolatedStringHand public static void IsEmpty(IEnumerable collection, [StringSyntax(StringSyntaxAttribute.CompositeFormat)] string? message, params object?[]? parameters) => HasCount("IsEmpty", 0, collection, message, parameters); + /// + /// Tests that the collection is empty. + /// + /// The collection. + /// The message format to display when the assertion fails. + public static void IsEmpty(IEnumerable collection, string? message) + => HasCount("IsEmpty", 0, collection, message); + private static void HasCount(string assertionName, int expected, IEnumerable collection, [StringSyntax(StringSyntaxAttribute.CompositeFormat)] string? message, params object?[]? parameters) { int actualCount = collection.Count(); @@ -301,6 +356,23 @@ private static void HasCount(string assertionName, int expected, IEnumerable< ThrowAssertCountFailed(assertionName, expected, actualCount, userMessage); } + private static void HasCount(string assertionName, int expected, IEnumerable collection, string? message) + { + int actualCount = 0; + foreach (object? item in collection) + { + actualCount++; + } + + if (actualCount == expected) + { + return; + } + + string userMessage = BuildUserMessage(message); + ThrowAssertCountFailed(assertionName, expected, actualCount, userMessage); + } + [DoesNotReturn] private static void ThrowAssertCountFailed(string assertionName, int expectedCount, int actualCount, string userMessage) { @@ -320,6 +392,6 @@ private static void ThrowAssertIsNotEmptyFailed(string userMessage) CultureInfo.CurrentCulture, FrameworkMessages.IsNotEmptyFailMsg, userMessage); - ThrowAssertFailed($"Assert.IsNotEmpty", finalMessage); + ThrowAssertFailed("Assert.IsNotEmpty", finalMessage); } } diff --git a/src/TestFramework/TestFramework/Assertions/Assert.EndsWith.cs b/src/TestFramework/TestFramework/Assertions/Assert.EndsWith.cs new file mode 100644 index 0000000000..43a93fe0c9 --- /dev/null +++ b/src/TestFramework/TestFramework/Assertions/Assert.EndsWith.cs @@ -0,0 +1,133 @@ +// 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.TestTools.UnitTesting; + +#pragma warning disable RS0026 // Do not add multiple public overloads with optional parameters + +public sealed partial class Assert +{ + #region EndsWith + + /// + /// Tests whether the specified string ends with the specified substring + /// and throws an exception if the test string does not end with the + /// substring. + /// + /// + /// The string expected to be a suffix of . + /// + /// + /// The string that is expected to end with . + /// + /// + /// The message to include in the exception when + /// does not end with . The message is + /// shown in test results. + /// + /// + /// is null, or is null, + /// or does not end with . + /// + public static void EndsWith([NotNull] string? substring, [NotNull] string? value, string message = "") + => EndsWith(substring, value, StringComparison.Ordinal, message); + + /// + /// Tests whether the specified string ends with the specified substring + /// and throws an exception if the test string does not end with the + /// substring. + /// + /// + /// The string expected to be a suffix of . + /// + /// + /// The string that is expected to end with . + /// + /// + /// The comparison method to compare strings . + /// + /// + /// The message to include in the exception when + /// does not end with . The message is + /// shown in test results. + /// + /// + /// is null, or is null, + /// or does not start with . + /// + public static void EndsWith([NotNull] string? substring, [NotNull] string? value, StringComparison comparisonType, string message = "") + { + CheckParameterNotNull(value, "Assert.EndsWith", "value", string.Empty); + CheckParameterNotNull(substring, "Assert.EndsWith", "substring", string.Empty); + if (!value.EndsWith(substring, comparisonType)) + { + string userMessage = BuildUserMessage(message); + string finalMessage = string.Format(CultureInfo.CurrentCulture, FrameworkMessages.EndsWithFail, value, substring, userMessage); + ThrowAssertFailed("Assert.EndsWith", finalMessage); + } + } + + #endregion // EndsWith + + #region DoesNotEndWith + + /// + /// Tests whether the specified string does not end with the specified substring + /// and throws an exception if the test string does not end with the + /// substring. + /// + /// + /// The string expected not to be a suffix of . + /// + /// + /// The string that is expected not to end with . + /// + /// + /// The message to include in the exception when + /// ends with . The message is + /// shown in test results. + /// + /// + /// is null, or is null, + /// or ends with . + /// + public static void DoesNotEndWith([NotNull] string? substring, [NotNull] string? value, string message = "") + => DoesNotEndWith(substring, value, StringComparison.Ordinal, message); + + /// + /// Tests whether the specified string does not end with the specified substring + /// and throws an exception if the test string does not end with the + /// substring. + /// + /// + /// The string expected not to be a suffix of . + /// + /// + /// The string that is expected not to end with . + /// + /// + /// The comparison method to compare strings . + /// + /// + /// The message to include in the exception when + /// ends with . The message is + /// shown in test results. + /// + /// + /// is null, or is null, + /// or ends with . + /// + public static void DoesNotEndWith([NotNull] string? substring, [NotNull] string? value, StringComparison comparisonType, string message = "") + { + CheckParameterNotNull(value, "Assert.DoesNotEndWith", "value", string.Empty); + CheckParameterNotNull(substring, "Assert.DoesNotEndWith", "substring", string.Empty); + if (value.EndsWith(substring, comparisonType)) + { + string userMessage = BuildUserMessage(message); + string finalMessage = string.Format(CultureInfo.CurrentCulture, FrameworkMessages.DoesNotEndWithFail, value, substring, userMessage); + ThrowAssertFailed("Assert.DoesNotEndWith", finalMessage); + } + } + + #endregion // DoesNotEndWith +} diff --git a/src/TestFramework/TestFramework/Assertions/Assert.IComparable.cs b/src/TestFramework/TestFramework/Assertions/Assert.IComparable.cs new file mode 100644 index 0000000000..3e27a0f99d --- /dev/null +++ b/src/TestFramework/TestFramework/Assertions/Assert.IComparable.cs @@ -0,0 +1,330 @@ +// 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.TestTools.UnitTesting; + +/// +/// A collection of helper classes to test various conditions within +/// unit tests. If the condition being tested is not met, an exception +/// is thrown. +/// +public sealed partial class Assert +{ + #region IsGreaterThan + + /// + /// Tests whether the value is greater than the lower bound and throws an exception + /// if it is not. + /// + /// + /// The type of values to compare. + /// + /// + /// The lower bound value that the value should exceed. + /// + /// + /// The value to compare. This is the value produced by the code under test. + /// + /// + /// The message to include in the exception when + /// is not greater than . The message is shown in + /// test results. + /// + /// + /// Thrown if is not greater than . + /// + public static void IsGreaterThan(T lowerBound, T value, string message = "") + where T : IComparable + { + if (value.CompareTo(lowerBound) > 0) + { + return; + } + + string userMessage = BuildUserMessage(message); + ThrowAssertIsGreaterThanFailed(lowerBound, value, userMessage); + } + + #endregion // IsGreaterThan + + #region IsGreaterThanOrEqualTo + + /// + /// Tests whether the value is greater than or equal to the lower bound and throws an exception + /// if it is not. + /// + /// + /// The type of values to compare. + /// + /// + /// The lower bound value that the value should meet or exceed. + /// + /// + /// The value to compare. This is the value produced by the code under test. + /// + /// + /// The message to include in the exception when + /// is not greater than or equal to . The message is shown in + /// test results. + /// + /// + /// Thrown if is not greater than or equal to . + /// + public static void IsGreaterThanOrEqualTo(T lowerBound, T value, string message = "") + where T : IComparable + { + if (value.CompareTo(lowerBound) >= 0) + { + return; + } + + string userMessage = BuildUserMessage(message); + ThrowAssertIsGreaterThanOrEqualToFailed(lowerBound, value, userMessage); + } + + #endregion // IsGreaterThanOrEqualTo + + #region IsLessThan + + /// + /// Tests whether the value is less than the upper bound and throws an exception + /// if it is not. + /// + /// + /// The type of values to compare. + /// + /// + /// The upper bound value that the value should be less than. + /// + /// + /// The value to compare. This is the value produced by the code under test. + /// + /// + /// The message to include in the exception when + /// is not less than . The message is shown in + /// test results. + /// + /// + /// Thrown if is not less than . + /// + public static void IsLessThan(T upperBound, T value, string message = "") + where T : IComparable + { + if (value.CompareTo(upperBound) < 0) + { + return; + } + + string userMessage = BuildUserMessage(message); + ThrowAssertIsLessThanFailed(upperBound, value, userMessage); + } + + #endregion // IsLessThan + + #region IsLessThanOrEqualTo + + /// + /// Tests whether the value is less than or equal to the upper bound and throws an exception + /// if it is not. + /// + /// + /// The type of values to compare. + /// + /// + /// The upper bound value that the value should not exceed. + /// + /// + /// The value to compare. This is the value produced by the code under test. + /// + /// + /// The message to include in the exception when + /// is not less than or equal to . The message is shown in + /// test results. + /// + /// + /// Thrown if is not less than or equal to . + /// + public static void IsLessThanOrEqualTo(T upperBound, T value, string message = "") + where T : IComparable + { + if (value.CompareTo(upperBound) <= 0) + { + return; + } + + string userMessage = BuildUserMessage(message); + ThrowAssertIsLessThanOrEqualToFailed(upperBound, value, userMessage); + } + + #endregion // IsLessThanOrEqualTo + + #region IsPositive + + /// + /// Tests whether the specified value is positive and throws an exception + /// if it is not. + /// + /// + /// The type of value to test. + /// + /// + /// The value to test. + /// + /// + /// The message to include in the exception when + /// is not positive. The message is shown in test results. + /// + /// + /// Thrown if is not positive. + /// + public static void IsPositive(T value, string message = "") + where T : struct, IComparable + { + var zero = default(T); + + // Handle special case for floating point NaN values + if (value is float.NaN) + { + string userMessage = BuildUserMessage(message); + ThrowAssertIsPositiveFailed(value, userMessage); + return; + } + + if (value is double.NaN) + { + string userMessage = BuildUserMessage(message); + ThrowAssertIsPositiveFailed(value, userMessage); + return; + } + + if (value.CompareTo(zero) > 0) + { + return; + } + + string userMessage2 = BuildUserMessage(message); + ThrowAssertIsPositiveFailed(value, userMessage2); + } + + #endregion // IsPositive + + #region IsNegative + + /// + /// Tests whether the specified value is negative and throws an exception + /// if it is not. + /// + /// + /// The type of value to test. + /// + /// + /// The value to test. + /// + /// + /// The message to include in the exception when + /// is not negative. The message is shown in test results. + /// + /// + /// Thrown if is not negative. + /// + public static void IsNegative(T value, string message = "") + where T : struct, IComparable + { + var zero = default(T); + + // Handle special case for floating point NaN values + if (value is float.NaN) + { + string userMessage = BuildUserMessage(message); + ThrowAssertIsNegativeFailed(value, userMessage); + return; + } + + if (value is double.NaN) + { + string userMessage = BuildUserMessage(message); + ThrowAssertIsNegativeFailed(value, userMessage); + return; + } + + if (value.CompareTo(zero) < 0) + { + return; + } + + string userMessage2 = BuildUserMessage(message); + ThrowAssertIsNegativeFailed(value, userMessage2); + } + + #endregion // IsNegative + + [DoesNotReturn] + private static void ThrowAssertIsGreaterThanFailed(T lowerBound, T value, string userMessage) + { + string finalMessage = string.Format( + CultureInfo.CurrentCulture, + FrameworkMessages.IsGreaterThanFailMsg, + userMessage, + ReplaceNulls(lowerBound), + ReplaceNulls(value)); + ThrowAssertFailed("Assert.IsGreaterThan", finalMessage); + } + + [DoesNotReturn] + private static void ThrowAssertIsGreaterThanOrEqualToFailed(T lowerBound, T value, string userMessage) + { + string finalMessage = string.Format( + CultureInfo.CurrentCulture, + FrameworkMessages.IsGreaterThanOrEqualToFailMsg, + userMessage, + ReplaceNulls(lowerBound), + ReplaceNulls(value)); + ThrowAssertFailed("Assert.IsGreaterThanOrEqualTo", finalMessage); + } + + [DoesNotReturn] + private static void ThrowAssertIsLessThanFailed(T upperBound, T value, string userMessage) + { + string finalMessage = string.Format( + CultureInfo.CurrentCulture, + FrameworkMessages.IsLessThanFailMsg, + userMessage, + ReplaceNulls(upperBound), + ReplaceNulls(value)); + ThrowAssertFailed("Assert.IsLessThan", finalMessage); + } + + [DoesNotReturn] + private static void ThrowAssertIsLessThanOrEqualToFailed(T upperBound, T value, string userMessage) + { + string finalMessage = string.Format( + CultureInfo.CurrentCulture, + FrameworkMessages.IsLessThanOrEqualToFailMsg, + userMessage, + ReplaceNulls(upperBound), + ReplaceNulls(value)); + ThrowAssertFailed("Assert.IsLessThanOrEqualTo", finalMessage); + } + + [DoesNotReturn] + private static void ThrowAssertIsPositiveFailed(T value, string userMessage) + { + string finalMessage = string.Format( + CultureInfo.CurrentCulture, + FrameworkMessages.IsPositiveFailMsg, + userMessage, + ReplaceNulls(value)); + ThrowAssertFailed("Assert.IsPositive", finalMessage); + } + + [DoesNotReturn] + private static void ThrowAssertIsNegativeFailed(T value, string userMessage) + { + string finalMessage = string.Format( + CultureInfo.CurrentCulture, + FrameworkMessages.IsNegativeFailMsg, + userMessage, + ReplaceNulls(value)); + ThrowAssertFailed("Assert.IsNegative", finalMessage); + } +} diff --git a/src/TestFramework/TestFramework/Assertions/Assert.Matches.cs b/src/TestFramework/TestFramework/Assertions/Assert.Matches.cs new file mode 100644 index 0000000000..224c4ecb86 --- /dev/null +++ b/src/TestFramework/TestFramework/Assertions/Assert.Matches.cs @@ -0,0 +1,135 @@ +// 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.TestTools.UnitTesting; + +#pragma warning disable RS0026 // Do not add multiple public overloads with optional parameters + +public sealed partial class Assert +{ + #region MatchesRegex + + /// + /// Tests whether the specified string MatchesRegex a regular expression and + /// throws an exception if the string does not match the expression. + /// + /// + /// The regular expression that is + /// expected to match. + /// + /// + /// The string that is expected to match . + /// + /// + /// The message to include in the exception when + /// does not match . The message is shown in + /// test results. + /// + /// + /// is null, or is null, + /// or does not match . + /// + public static void MatchesRegex([NotNull] Regex? pattern, [NotNull] string? value, string message = "") + { + CheckParameterNotNull(value, "Assert.MatchesRegex", "value", string.Empty); + CheckParameterNotNull(pattern, "Assert.MatchesRegex", "pattern", string.Empty); + + if (!pattern.IsMatch(value)) + { + string userMessage = BuildUserMessage(message); + string finalMessage = string.Format(CultureInfo.CurrentCulture, FrameworkMessages.IsMatchFail, value, pattern, userMessage); + ThrowAssertFailed("Assert.MatchesRegex", finalMessage); + } + } + + /// + /// Tests whether the specified string MatchesRegex a regular expression and + /// throws an exception if the string does not match the expression. + /// + /// + /// The regular expression that is + /// expected to match. + /// + /// + /// The string that is expected to match . + /// + /// + /// The message to include in the exception when + /// does not match . The message is shown in + /// test results. + /// + /// + /// is null, or is null, + /// or does not match . + /// + public static void MatchesRegex([NotNull] string? pattern, [NotNull] string? value, string message = "") + => MatchesRegex(ToRegex(pattern), value, message); + + #endregion // MatchesRegex + + #region DoesNotMatchRegex + + /// + /// Tests whether the specified string does not match a regular expression + /// and throws an exception if the string MatchesRegex the expression. + /// + /// + /// The regular expression that is + /// expected to not match. + /// + /// + /// The string that is expected not to match . + /// + /// + /// The message to include in the exception when + /// MatchesRegex . The message is shown in test + /// results. + /// + /// + /// is null, or is null, + /// or MatchesRegex . + /// + public static void DoesNotMatchRegex([NotNull] Regex? pattern, [NotNull] string? value, string message = "") + { + CheckParameterNotNull(value, "Assert.DoesNotMatchRegex", "value", string.Empty); + CheckParameterNotNull(pattern, "Assert.DoesNotMatchRegex", "pattern", string.Empty); + + if (pattern.IsMatch(value)) + { + string userMessage = BuildUserMessage(message); + string finalMessage = string.Format(CultureInfo.CurrentCulture, FrameworkMessages.IsNotMatchFail, value, pattern, userMessage); + ThrowAssertFailed("Assert.DoesNotMatchRegex", finalMessage); + } + } + + /// + /// Tests whether the specified string does not match a regular expression + /// and throws an exception if the string MatchesRegex the expression. + /// + /// + /// The regular expression that is + /// expected to not match. + /// + /// + /// The string that is expected not to match . + /// + /// + /// The message to include in the exception when + /// MatchesRegex . The message is shown in test + /// results. + /// + /// + /// is null, or is null, + /// or MatchesRegex . + /// + public static void DoesNotMatchRegex([NotNull] string? pattern, [NotNull] string? value, string message = "") + => DoesNotMatchRegex(ToRegex(pattern), value, message); + + #endregion // DoesNotMatchRegex + + private static Regex? ToRegex([NotNull] string? pattern) + { + CheckParameterNotNull(pattern, "Assert.MatchesRegex", "pattern", string.Empty); + return new Regex(pattern); + } +} diff --git a/src/TestFramework/TestFramework/Assertions/Assert.StartsWith.cs b/src/TestFramework/TestFramework/Assertions/Assert.StartsWith.cs new file mode 100644 index 0000000000..9f6904a939 --- /dev/null +++ b/src/TestFramework/TestFramework/Assertions/Assert.StartsWith.cs @@ -0,0 +1,131 @@ +// 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.TestTools.UnitTesting; + +#pragma warning disable RS0026 // Do not add multiple public overloads with optional parameters + +public sealed partial class Assert +{ + #region StartsWith + + /// + /// Tests whether the specified string begins with the specified substring + /// and throws an exception if the test string does not start with the + /// substring. + /// + /// + /// The string expected to be a prefix of . + /// + /// + /// The string that is expected to begin with . + /// + /// + /// The message to include in the exception when + /// does not begin with . The message is + /// shown in test results. + /// + /// + /// is null, or is null, + /// or does not start with . + /// + public static void StartsWith([NotNull] string? substring, [NotNull] string? value, string message = "") + => StartsWith(substring, value, StringComparison.Ordinal, message); + + /// + /// Tests whether the specified string begins with the specified substring + /// and throws an exception if the test string does not start with the + /// substring. + /// + /// + /// The string expected to be a prefix of . + /// + /// + /// The string that is expected to begin with . + /// + /// + /// The comparison method to compare strings . + /// + /// + /// The message to include in the exception when + /// does not begin with . The message is + /// shown in test results. + /// + /// + /// is null, or is null, + /// or does not start with . + /// + public static void StartsWith([NotNull] string? substring, [NotNull] string? value, StringComparison comparisonType, string message = "") + { + CheckParameterNotNull(value, "Assert.StartsWith", "value", string.Empty); + CheckParameterNotNull(substring, "Assert.StartsWith", "substring", string.Empty); + if (!value.StartsWith(substring, comparisonType)) + { + string userMessage = BuildUserMessage(message); + string finalMessage = string.Format(CultureInfo.CurrentCulture, FrameworkMessages.StartsWithFail, value, substring, userMessage); + ThrowAssertFailed("Assert.StartsWith", finalMessage); + } + } + + #endregion // StartsWith + + #region DoesNotStartWith + + /// + /// Tests whether the specified string does not begin with the specified substring + /// and throws an exception if the test string does start with the substring. + /// + /// + /// The string expected to be a prefix of . + /// + /// + /// The string that is expected to begin with . + /// + /// + /// The message to include in the exception when + /// does not begin with . The message is + /// shown in test results. + /// + /// + /// is null, or is null, + /// or does not start with . + /// + public static void DoesNotStartWith([NotNull] string? substring, [NotNull] string? value, string message = "") + => DoesNotStartWith(substring, value, StringComparison.Ordinal, message); + + /// + /// Tests whether the specified string does not begin with the specified substring + /// and throws an exception if the test string does start with the substring. + /// + /// + /// The string expected to be a prefix of . + /// + /// + /// The string that is expected to begin with . + /// + /// + /// The comparison method to compare strings . + /// + /// + /// The message to include in the exception when + /// does not begin with . The message is + /// shown in test results. + /// + /// + /// is null, or is null, + /// or does not start with . + /// + public static void DoesNotStartWith([NotNull] string? substring, [NotNull] string? value, StringComparison comparisonType, string message = "") + { + CheckParameterNotNull(value, "Assert.DoesNotStartWith", "value", string.Empty); + CheckParameterNotNull(substring, "Assert.DoesNotStartWith", "substring", string.Empty); + if (value.StartsWith(substring, comparisonType)) + { + string userMessage = BuildUserMessage(message); + string finalMessage = string.Format(CultureInfo.CurrentCulture, FrameworkMessages.DoesNotStartWithFail, value, substring, userMessage); + ThrowAssertFailed("Assert.DoesNotStartWith", finalMessage); + } + } + + #endregion // DoesNotStartWith +} diff --git a/src/TestFramework/TestFramework/Assertions/Assert.ThrowsException.cs b/src/TestFramework/TestFramework/Assertions/Assert.ThrowsException.cs index d57d2c31c9..b02272b50a 100644 --- a/src/TestFramework/TestFramework/Assertions/Assert.ThrowsException.cs +++ b/src/TestFramework/TestFramework/Assertions/Assert.ThrowsException.cs @@ -31,6 +31,11 @@ public AssertNonStrictThrowsInterpolatedStringHandler(int literalLength, int for } } + public AssertNonStrictThrowsInterpolatedStringHandler(int literalLength, int formattedCount, Func action, out bool shouldAppend) + : this(literalLength, formattedCount, (Action)(() => _ = action()), out shouldAppend) + { + } + internal TException ComputeAssertion() { if (_state.FailAction is not null) @@ -98,6 +103,11 @@ public AssertThrowsExactlyInterpolatedStringHandler(int literalLength, int forma } } + public AssertThrowsExactlyInterpolatedStringHandler(int literalLength, int formattedCount, Func action, out bool shouldAppend) + : this(literalLength, formattedCount, (Action)(() => _ = action()), out shouldAppend) + { + } + internal TException ComputeAssertion() { if (_state.FailAction is not null) @@ -218,6 +228,13 @@ public static TException Throws(Action action, [InterpolatedStringHa where TException : Exception => message.ComputeAssertion(); + /// +#pragma warning disable IDE0060 // Remove unused parameter - https://github.com/dotnet/roslyn/issues/76578 + public static TException Throws(Func action, [InterpolatedStringHandlerArgument(nameof(action))] ref AssertNonStrictThrowsInterpolatedStringHandler message) +#pragma warning restore IDE0060 // Remove unused parameter + where TException : Exception + => message.ComputeAssertion(); + /// /// Asserts that the delegate throws an exception of type /// (and not of derived type) and throws AssertFailedException if code does not throws exception or throws @@ -288,6 +305,13 @@ public static TException ThrowsExactly(Action action, [InterpolatedS where TException : Exception => message.ComputeAssertion(); + /// +#pragma warning disable IDE0060 // Remove unused parameter - https://github.com/dotnet/roslyn/issues/76578 + public static TException ThrowsExactly(Func action, [InterpolatedStringHandlerArgument(nameof(action))] ref AssertThrowsExactlyInterpolatedStringHandler message) +#pragma warning restore IDE0060 // Remove unused parameter + where TException : Exception + => message.ComputeAssertion(); + /// /// Tests whether the code specified by delegate throws exact given exception /// of type (and not of derived type) and throws AssertFailedException diff --git a/src/TestFramework/TestFramework/Assertions/Assert.cs b/src/TestFramework/TestFramework/Assertions/Assert.cs index 61ebec34ae..9d5e9ccdae 100644 --- a/src/TestFramework/TestFramework/Assertions/Assert.cs +++ b/src/TestFramework/TestFramework/Assertions/Assert.cs @@ -23,7 +23,7 @@ private Assert() /// Users could then use a syntax similar to the default assertions which in this case is "Assert.That.IsOfType<Dog>(animal);" /// More documentation is at "https://github.com/Microsoft/testfx/docs/README.md". /// - public static Assert That { get; } = new Assert(); + public static Assert That { get; } = new(); /// /// Replaces null characters ('\0') with "\\0". @@ -139,24 +139,53 @@ private static int CompareInternal(string? expected, string? actual, bool ignore => string.Compare(expected, actual, ignoreCase, culture); #pragma warning restore CA1309 // Use ordinal string comparison - #region EqualsAssertion + #region DoNotUse /// /// Static equals overloads are used for comparing instances of two types for reference /// equality. This method should not be used for comparison of two instances for - /// equality. This object will always throw with Assert.Fail. Please use - /// Assert.AreEqual and associated overloads in your unit tests. + /// equality. Please use Assert.AreEqual and associated overloads in your unit tests. /// /// Object A. /// Object B. - /// False, always. + /// Never returns. [SuppressMessage("Microsoft.Naming", "CA1720:IdentifiersShouldNotContainTypeNames", MessageId = "obj", Justification = "We want to compare 'object A' with 'object B', so it makes sense to have 'obj' in the parameter name")] -#pragma warning disable IDE0060 // Remove unused parameter + [Obsolete( + FrameworkConstants.DoNotUseAssertEquals, +#if DEBUG + error: false)] +#else + error: true)] +#endif + [DoesNotReturn] public static new bool Equals(object? objA, object? objB) -#pragma warning restore IDE0060 // Remove unused parameter { Fail(FrameworkMessages.DoNotUseAssertEquals); return false; } + + /// + /// Static ReferenceEquals overloads are used for comparing instances of two types for reference + /// equality. This method should not be used for comparison of two instances for + /// reference equality. Please use Assert.AreSame and associated overloads in your unit tests. + /// + /// Object A. + /// Object B. + /// Never returns. + [SuppressMessage("Microsoft.Naming", "CA1720:IdentifiersShouldNotContainTypeNames", MessageId = "obj", Justification = "We want to compare 'object A' with 'object B', so it makes sense to have 'obj' in the parameter name")] + [Obsolete( + FrameworkConstants.DoNotUseAssertReferenceEquals, +#if DEBUG + error: false)] +#else + error: true)] +#endif + [DoesNotReturn] + public static new bool ReferenceEquals(object? objA, object? objB) + { + Fail(FrameworkMessages.DoNotUseAssertReferenceEquals); + return false; + } + #endregion } diff --git a/src/TestFramework/TestFramework/Assertions/CollectionAssert.cs b/src/TestFramework/TestFramework/Assertions/CollectionAssert.cs index 0d87f35366..b0534331f5 100644 --- a/src/TestFramework/TestFramework/Assertions/CollectionAssert.cs +++ b/src/TestFramework/TestFramework/Assertions/CollectionAssert.cs @@ -25,7 +25,7 @@ private CollectionAssert() /// Users could then use a syntax similar to the default assertions which in this case is "CollectionAssert.That.AreEqualUnordered(list1, list2);" /// More documentation is at "https://github.com/Microsoft/testfx/docs/README.md". /// - public static CollectionAssert That { get; } = new CollectionAssert(); + public static CollectionAssert That { get; } = new(); #endregion @@ -99,7 +99,7 @@ public static void Contains([NotNull] ICollection? collection, object? element, foreach (object? current in collection) { - if (Equals(current, element)) + if (object.Equals(current, element)) { return; } @@ -176,7 +176,7 @@ public static void DoesNotContain([NotNull] ICollection? collection, object? ele foreach (object? current in collection) { - if (Equals(current, element)) + if (object.Equals(current, element)) { Assert.ThrowAssertFailed("CollectionAssert.DoesNotContain", Assert.BuildUserMessage(message, parameters)); } @@ -327,8 +327,7 @@ public static void AllItemsAreUnique([NotNull] ICollection? collection, [StringS } else { -#pragma warning disable CA1864 // Prefer the 'IDictionary.TryAdd(TKey, TValue)' method - if (table.ContainsKey(current)) + if (!table.TryAdd(current, true)) { string userMessage = Assert.BuildUserMessage(message, parameters); string finalMessage = string.Format( @@ -339,11 +338,6 @@ public static void AllItemsAreUnique([NotNull] ICollection? collection, [StringS Assert.ThrowAssertFailed("CollectionAssert.AllItemsAreUnique", finalMessage); } - else - { - table.Add(current, true); - } -#pragma warning restore CA1864 // Prefer the 'IDictionary.TryAdd(TKey, TValue)' method } } } @@ -424,9 +418,21 @@ public static void IsSubsetOf([NotNull] ICollection? subset, [NotNull] ICollecti { Assert.CheckParameterNotNull(subset, "CollectionAssert.IsSubsetOf", "subset", string.Empty); Assert.CheckParameterNotNull(superset, "CollectionAssert.IsSubsetOf", "superset", string.Empty); - if (!IsSubsetOfHelper(subset, superset)) + Tuple> isSubsetValue = IsSubsetOfHelper(subset, superset); + if (!isSubsetValue.Item1) { - Assert.ThrowAssertFailed("CollectionAssert.IsSubsetOf", Assert.BuildUserMessage(message, parameters)); + string returnedSubsetValueMessage = string.Join(", ", isSubsetValue.Item2.Select(item => Convert.ToString(item, CultureInfo.InvariantCulture))); + + returnedSubsetValueMessage = string.Format(CultureInfo.InvariantCulture, FrameworkMessages.ReturnedSubsetValueMessage, returnedSubsetValueMessage); + string userMessage = Assert.BuildUserMessage(message, parameters); + if (string.IsNullOrEmpty(userMessage)) + { + Assert.ThrowAssertFailed("CollectionAssert.IsSubsetOf", returnedSubsetValueMessage); + } + else + { + Assert.ThrowAssertFailed("CollectionAssert.IsSubsetOf", $"{returnedSubsetValueMessage} {userMessage}"); + } } } @@ -499,7 +505,8 @@ public static void IsNotSubsetOf([NotNull] ICollection? subset, [NotNull] IColle { Assert.CheckParameterNotNull(subset, "CollectionAssert.IsNotSubsetOf", "subset", string.Empty); Assert.CheckParameterNotNull(superset, "CollectionAssert.IsNotSubsetOf", "superset", string.Empty); - if (IsSubsetOfHelper(subset, superset)) + Tuple> isSubsetValue = IsSubsetOfHelper(subset, superset); + if (isSubsetValue.Item1) { Assert.ThrowAssertFailed("CollectionAssert.IsNotSubsetOf", Assert.BuildUserMessage(message, parameters)); } @@ -691,7 +698,7 @@ public static void AreEquivalent( } // If the references are the same or both collections are null, they are equivalent. - if (ReferenceEquals(expected, actual) || expected == null) + if (object.ReferenceEquals(expected, actual) || expected == null) { return; } @@ -926,7 +933,7 @@ public static void AreNotEquivalent( // If the references are the same or both collections are null, they // are equivalent. object.ReferenceEquals will handle case where both are null. - if (ReferenceEquals(expected, actual)) + if (object.ReferenceEquals(expected, actual)) { string userMessage = Assert.BuildUserMessage(message, parameters); string finalMessage = string.Format( @@ -1444,21 +1451,30 @@ public static void AreNotEqual(ICollection? notExpected, ICollection? actual, [N /// True if is a subset of /// , false otherwise. /// - internal static bool IsSubsetOfHelper(ICollection subset, ICollection superset) + internal static Tuple> IsSubsetOfHelper(ICollection subset, ICollection superset) { // $ CONSIDER: The current algorithm counts the number of occurrences of each // $ CONSIDER: element in each collection and then compares the count, resulting // $ CONSIDER: in an algorithm of ~n*log(n) + m*log(m) + n*log(m). It should be // $ CONSIDER: faster to sort both collections and do an element-by-element // $ CONSIDER: comparison, which should result in ~n*log(n) + m*log(m) + n. + var nonSubsetValues = new List(); // Count the occurrences of each object in both collections. Dictionary subsetElements = GetElementCounts(subset.Cast(), EqualityComparer.Default, out int subsetNulls); Dictionary supersetElements = GetElementCounts(superset.Cast(), EqualityComparer.Default, out int supersetNulls); + bool isSubset = true; + + // Check null counts first if (subsetNulls > supersetNulls) { - return false; + isSubset = false; + // Add the excess null values to non-subset collection + for (int i = 0; i < (subsetNulls - supersetNulls); i++) + { + nonSubsetValues.Add(null); + } } // Compare the counts of each object in the subset to the count of that object @@ -1470,12 +1486,17 @@ internal static bool IsSubsetOfHelper(ICollection subset, ICollection superset) if (subsetCount > supersetCount) { - return false; + isSubset = false; + // Add the excess occurrences to non-subset collection + int excessCount = subsetCount - supersetCount; + for (int i = 0; i < excessCount; i++) + { + nonSubsetValues.Add(element); + } } } - // All the elements counts were OK. - return true; + return new Tuple>(isSubset, nonSubsetValues); } #pragma warning disable CS8714 @@ -1598,7 +1619,7 @@ private static bool AreCollectionsEqual(ICollection? expected, ICollection? actu ref string reason) { Assert.CheckParameterNotNull(comparer, "Assert.AreCollectionsEqual", "comparer", string.Empty); - if (ReferenceEquals(expected, actual)) + if (object.ReferenceEquals(expected, actual)) { reason = string.Format(CultureInfo.CurrentCulture, FrameworkMessages.BothCollectionsSameReference, string.Empty); return true; @@ -1685,4 +1706,54 @@ private sealed class ObjectComparer : IComparer int IComparer.Compare(object? x, object? y) => Equals(x, y) ? 0 : -1; } #endregion + + #region DoNotUse + + /// + /// Static equals overloads are used for comparing instances of two types for equality. + /// This method should not be used for comparison of two instances for equality. + /// Please use CollectionAssert.AreEqual and associated overloads in your unit tests. + /// + /// Object A. + /// Object B. + /// Never returns. + [SuppressMessage("Microsoft.Naming", "CA1720:IdentifiersShouldNotContainTypeNames", MessageId = "obj", Justification = "We want to compare 'object A' with 'object B', so it makes sense to have 'obj' in the parameter name")] + [Obsolete( + FrameworkConstants.DoNotUseCollectionAssertEquals, +#if DEBUG + error: false)] +#else + error: true)] +#endif + [DoesNotReturn] + public static new bool Equals(object? objA, object? objB) + { + Assert.Fail(FrameworkMessages.DoNotUseCollectionAssertEquals); + return false; + } + + /// + /// Static ReferenceEquals overloads are used for comparing instances of two types for reference + /// equality. This method should not be used for comparison of two instances for + /// reference equality. Please use CollectionAssert methods or Assert.AreSame and associated overloads in your unit tests. + /// + /// Object A. + /// Object B. + /// Never returns. + [SuppressMessage("Microsoft.Naming", "CA1720:IdentifiersShouldNotContainTypeNames", MessageId = "obj", Justification = "We want to compare 'object A' with 'object B', so it makes sense to have 'obj' in the parameter name")] + [Obsolete( + FrameworkConstants.DoNotUseCollectionAssertReferenceEquals, +#if DEBUG + error: false)] +#else + error: true)] +#endif + [DoesNotReturn] + public static new bool ReferenceEquals(object? objA, object? objB) + { + Assert.Fail(FrameworkMessages.DoNotUseCollectionAssertReferenceEquals); + return false; + } + + #endregion } diff --git a/src/TestFramework/TestFramework/Assertions/StringAssert.cs b/src/TestFramework/TestFramework/Assertions/StringAssert.cs index a55c653c4f..741076b7c6 100644 --- a/src/TestFramework/TestFramework/Assertions/StringAssert.cs +++ b/src/TestFramework/TestFramework/Assertions/StringAssert.cs @@ -25,7 +25,7 @@ private StringAssert() /// Users could then use a syntax similar to the default assertions which in this case is "StringAssert.That.ContainsWords(value, substrings);" /// More documentation is at "https://github.com/Microsoft/testfx/docs/README.md". /// - public static StringAssert That { get; } = new StringAssert(); + public static StringAssert That { get; } = new(); #endregion @@ -657,4 +657,54 @@ public static void DoesNotMatch([NotNull] string? value, [NotNull] Regex? patter } #endregion Regular Expressions + + #region DoNotUse + + /// + /// Static equals overloads are used for comparing instances of two types for equality. + /// This method should not be used for comparison of two instances for equality. + /// Please use StringAssert methods or Assert.AreEqual and associated overloads in your unit tests. + /// + /// Object A. + /// Object B. + /// Never returns. + [SuppressMessage("Microsoft.Naming", "CA1720:IdentifiersShouldNotContainTypeNames", MessageId = "obj", Justification = "We want to compare 'object A' with 'object B', so it makes sense to have 'obj' in the parameter name")] + [Obsolete( + FrameworkConstants.DoNotUseStringAssertEquals, +#if DEBUG + error: false)] +#else + error: true)] +#endif + [DoesNotReturn] + public static new bool Equals(object? objA, object? objB) + { + Assert.Fail(FrameworkMessages.DoNotUseStringAssertEquals); + return false; + } + + /// + /// Static ReferenceEquals overloads are used for comparing instances of two types for reference + /// equality. This method should not be used for comparison of two instances for + /// reference equality. Please use StringAssert methods or Assert.AreSame and associated overloads in your unit tests. + /// + /// Object A. + /// Object B. + /// Never returns. + [SuppressMessage("Microsoft.Naming", "CA1720:IdentifiersShouldNotContainTypeNames", MessageId = "obj", Justification = "We want to compare 'object A' with 'object B', so it makes sense to have 'obj' in the parameter name")] + [Obsolete( + FrameworkConstants.DoNotUseStringAssertReferenceEquals, +#if DEBUG + error: false)] +#else + error: true)] +#endif + [DoesNotReturn] + public static new bool ReferenceEquals(object? objA, object? objB) + { + Assert.Fail(FrameworkMessages.DoNotUseStringAssertReferenceEquals); + return false; + } + + #endregion } diff --git a/src/TestFramework/TestFramework/Attributes/DataSource/DataTestMethodAttribute.cs b/src/TestFramework/TestFramework/Attributes/DataSource/DataTestMethodAttribute.cs index ed2e10f86f..d93c2161ee 100644 --- a/src/TestFramework/TestFramework/Attributes/DataSource/DataTestMethodAttribute.cs +++ b/src/TestFramework/TestFramework/Attributes/DataSource/DataTestMethodAttribute.cs @@ -13,7 +13,7 @@ namespace Microsoft.VisualStudio.TestTools.UnitTesting; [EditorBrowsable(EditorBrowsableState.Never)] public class DataTestMethodAttribute : TestMethodAttribute { - private protected override bool UseAsync => true; + private protected override bool UseAsync => GetType() == typeof(DataTestMethodAttribute); /// /// Initializes a new instance of the class. diff --git a/src/TestFramework/TestFramework/Attributes/DataSource/DynamicDataAttribute.cs b/src/TestFramework/TestFramework/Attributes/DataSource/DynamicDataAttribute.cs index 900906b127..a05d089798 100644 --- a/src/TestFramework/TestFramework/Attributes/DataSource/DynamicDataAttribute.cs +++ b/src/TestFramework/TestFramework/Attributes/DataSource/DynamicDataAttribute.cs @@ -8,7 +8,7 @@ namespace Microsoft.VisualStudio.TestTools.UnitTesting; /// -/// Enum to specify whether the data is stored as property or in method. +/// Enum to specify whether the data is stored as property, in method, or in field. /// public enum DynamicDataSourceType { @@ -26,6 +26,11 @@ public enum DynamicDataSourceType /// The data source type is auto-detected. /// AutoDetect = 2, + + /// + /// Data is declared as field. + /// + Field = 3, } /// @@ -36,17 +41,17 @@ public sealed class DynamicDataAttribute : Attribute, ITestDataSource, ITestData { private readonly string _dynamicDataSourceName; private readonly DynamicDataSourceType _dynamicDataSourceType; - + private readonly object?[] _dynamicDataSourceArguments = []; private readonly Type? _dynamicDataDeclaringType; /// /// Initializes a new instance of the class. /// /// - /// The name of method or property having test data. + /// The name of method, property, or field having test data. /// /// - /// Specifies whether the data is stored as property or in method. + /// Specifies whether the data is stored as property, in method, or in field. /// [EditorBrowsable(EditorBrowsableState.Never)] public DynamicDataAttribute(string dynamicDataSourceName, DynamicDataSourceType dynamicDataSourceType) @@ -59,7 +64,7 @@ public DynamicDataAttribute(string dynamicDataSourceName, DynamicDataSourceType /// Initializes a new instance of the class. /// /// - /// The name of method or property having test data. + /// The name of method, property, or field having test data. /// public DynamicDataAttribute(string dynamicDataSourceName) { @@ -67,19 +72,35 @@ public DynamicDataAttribute(string dynamicDataSourceName) _dynamicDataSourceType = DynamicDataSourceType.AutoDetect; } + /// + /// Initializes a new instance of the class. + /// + /// + /// The name of method, property, or field having test data. + /// + /// + /// Arguments to be passed to method referred to by . + /// + public DynamicDataAttribute(string dynamicDataSourceName, params object?[] dynamicDataSourceArguments) + { + _dynamicDataSourceName = dynamicDataSourceName; + _dynamicDataSourceType = DynamicDataSourceType.AutoDetect; + _dynamicDataSourceArguments = dynamicDataSourceArguments; + } + /// /// Initializes a new instance of the class when the test data is present in a class different /// from test method's class. /// /// - /// The name of method or property having test data. + /// The name of method, property, or field having test data. /// /// - /// The declaring type of property or method having data. Useful in cases when declaring type is present in a class different from + /// The declaring type of property, method, or field having data. Useful in cases when declaring type is present in a class different from /// test method's class. If null, declaring type defaults to test method's class type. /// /// - /// Specifies whether the data is stored as property or in method. + /// Specifies whether the data is stored as property, in method, or in field. /// [EditorBrowsable(EditorBrowsableState.Never)] public DynamicDataAttribute(string dynamicDataSourceName, Type dynamicDataDeclaringType, DynamicDataSourceType dynamicDataSourceType) @@ -90,15 +111,36 @@ public DynamicDataAttribute(string dynamicDataSourceName, Type dynamicDataDeclar /// from test method's class. /// /// - /// The name of method or property having test data. + /// The name of method, property, or field having test data. /// /// - /// The declaring type of property or method having data. Useful in cases when declaring type is present in a class different from + /// The declaring type of property, method, or field having data. Useful in cases when declaring type is present in a class different from /// test method's class. If null, declaring type defaults to test method's class type. /// public DynamicDataAttribute(string dynamicDataSourceName, Type dynamicDataDeclaringType) : this(dynamicDataSourceName) => _dynamicDataDeclaringType = dynamicDataDeclaringType; + /// + /// Initializes a new instance of the class when the test data is present in a class different + /// from test method's class. + /// + /// + /// The name of method, property, or field having test data. + /// + /// + /// The declaring type of property, method, or field having data. Useful in cases when declaring type is present in a class different from + /// test method's class. If null, declaring type defaults to test method's class type. + /// + /// + /// Arguments to be passed to method referred to by . + /// + public DynamicDataAttribute(string dynamicDataSourceName, Type dynamicDataDeclaringType, params object?[] dynamicDataSourceArguments) + : this(dynamicDataSourceName) + { + _dynamicDataDeclaringType = dynamicDataDeclaringType; + _dynamicDataSourceArguments = dynamicDataSourceArguments; + } + internal static TestIdGenerationStrategy TestIdGenerationStrategy { get; set; } /// @@ -121,7 +163,7 @@ public DynamicDataAttribute(string dynamicDataSourceName, Type dynamicDataDeclar /// public IEnumerable GetData(MethodInfo methodInfo) - => DynamicDataOperations.GetData(_dynamicDataDeclaringType, _dynamicDataSourceType, _dynamicDataSourceName, methodInfo); + => DynamicDataOperations.GetData(_dynamicDataDeclaringType, _dynamicDataSourceType, _dynamicDataSourceName, _dynamicDataSourceArguments, methodInfo); /// public string? GetDisplayName(MethodInfo methodInfo, object?[]? data) diff --git a/src/TestFramework/TestFramework/Attributes/DataSource/DynamicDataOperations.cs b/src/TestFramework/TestFramework/Attributes/DataSource/DynamicDataOperations.cs index 1c91754b59..b3990377c7 100644 --- a/src/TestFramework/TestFramework/Attributes/DataSource/DynamicDataOperations.cs +++ b/src/TestFramework/TestFramework/Attributes/DataSource/DynamicDataOperations.cs @@ -7,45 +7,56 @@ internal static class DynamicDataOperations { private const BindingFlags DeclaredOnlyLookup = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static | BindingFlags.DeclaredOnly; - public static IEnumerable GetData(Type? _dynamicDataDeclaringType, DynamicDataSourceType _dynamicDataSourceType, string _dynamicDataSourceName, MethodInfo methodInfo) + public static IEnumerable GetData(Type? dynamicDataDeclaringType, DynamicDataSourceType dynamicDataSourceType, string dynamicDataSourceName, object?[] dynamicDataSourceArguments, MethodInfo methodInfo) { // Check if the declaring type of test data is passed in. If not, default to test method's class type. - _dynamicDataDeclaringType ??= methodInfo.DeclaringType; - DebugEx.Assert(_dynamicDataDeclaringType is not null, "Declaring type of test data cannot be null."); + dynamicDataDeclaringType ??= methodInfo.DeclaringType; + DebugEx.Assert(dynamicDataDeclaringType is not null, "Declaring type of test data cannot be null."); object? obj = null; - switch (_dynamicDataSourceType) + switch (dynamicDataSourceType) { case DynamicDataSourceType.AutoDetect: #pragma warning disable IDE0045 // Convert to conditional expression - it becomes less readable. - if (GetPropertyConsideringInheritance(_dynamicDataDeclaringType, _dynamicDataSourceName) is { } dynamicDataPropertyInfo) + if (GetPropertyConsideringInheritance(dynamicDataDeclaringType, dynamicDataSourceName) is { } dynamicDataPropertyInfo) { obj = GetDataFromProperty(dynamicDataPropertyInfo); } - else if (GetMethodConsideringInheritance(_dynamicDataDeclaringType, _dynamicDataSourceName) is { } dynamicDataMethodInfo) + else if (GetMethodConsideringInheritance(dynamicDataDeclaringType, dynamicDataSourceName) is { } dynamicDataMethodInfo) { - obj = GetDataFromMethod(dynamicDataMethodInfo); + obj = GetDataFromMethod(dynamicDataMethodInfo, dynamicDataSourceArguments); + } + else if (GetFieldConsideringInheritance(dynamicDataDeclaringType, dynamicDataSourceName) is { } dynamicDataFieldInfo) + { + obj = GetDataFromField(dynamicDataFieldInfo); } else { - throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, FrameworkMessages.DynamicDataSourceShouldExistAndBeValid, _dynamicDataSourceName, _dynamicDataDeclaringType.FullName)); + throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, FrameworkMessages.DynamicDataSourceShouldExistAndBeValid, dynamicDataSourceName, dynamicDataDeclaringType.FullName)); } #pragma warning restore IDE0045 // Convert to conditional expression break; case DynamicDataSourceType.Property: - PropertyInfo property = GetPropertyConsideringInheritance(_dynamicDataDeclaringType, _dynamicDataSourceName) - ?? throw new ArgumentNullException($"{DynamicDataSourceType.Property} {_dynamicDataSourceName}"); + PropertyInfo property = GetPropertyConsideringInheritance(dynamicDataDeclaringType, dynamicDataSourceName) + ?? throw new ArgumentNullException($"{DynamicDataSourceType.Property} {dynamicDataSourceName}"); obj = GetDataFromProperty(property); break; case DynamicDataSourceType.Method: - MethodInfo method = GetMethodConsideringInheritance(_dynamicDataDeclaringType, _dynamicDataSourceName) - ?? throw new ArgumentNullException($"{DynamicDataSourceType.Method} {_dynamicDataSourceName}"); + MethodInfo method = GetMethodConsideringInheritance(dynamicDataDeclaringType, dynamicDataSourceName) + ?? throw new ArgumentNullException($"{DynamicDataSourceType.Method} {dynamicDataSourceName}"); + + obj = GetDataFromMethod(method, dynamicDataSourceArguments); + break; + + case DynamicDataSourceType.Field: + FieldInfo field = GetFieldConsideringInheritance(dynamicDataDeclaringType, dynamicDataSourceName) + ?? throw new ArgumentNullException($"{DynamicDataSourceType.Field} {dynamicDataSourceName}"); - obj = GetDataFromMethod(method); + obj = GetDataFromField(field); break; } @@ -55,8 +66,8 @@ public static IEnumerable GetData(Type? _dynamicDataDeclaringType, Dyn string.Format( CultureInfo.InvariantCulture, FrameworkMessages.DynamicDataValueNull, - _dynamicDataSourceName, - _dynamicDataDeclaringType.FullName)); + dynamicDataSourceName, + dynamicDataDeclaringType.FullName)); } if (!TryGetData(obj, out IEnumerable? data)) @@ -65,29 +76,55 @@ public static IEnumerable GetData(Type? _dynamicDataDeclaringType, Dyn string.Format( CultureInfo.InvariantCulture, FrameworkMessages.DynamicDataIEnumerableNull, - _dynamicDataSourceName, - _dynamicDataDeclaringType.FullName)); + dynamicDataSourceName, + dynamicDataDeclaringType.FullName)); } // Data is valid, return it. return data; } - private static object? GetDataFromMethod(MethodInfo method) + private static object? GetDataFromMethod(MethodInfo method, object?[] arguments) { - if (!method.IsStatic - || method.ContainsGenericParameters - || method.GetParameters().Length > 0) + if (!method.IsStatic || method.ContainsGenericParameters) { throw new NotSupportedException( string.Format( CultureInfo.InvariantCulture, - FrameworkMessages.DynamicDataInvalidPropertyLayout, + FrameworkMessages.DynamicDataInvalidMethodLayout, method.DeclaringType?.FullName is { } typeFullName ? $"{typeFullName}.{method.Name}" : method.Name)); } - // Note: the method is static and takes no parameters. - return method.Invoke(null, null); + ParameterInfo[] methodParameters = method.GetParameters(); + ParameterInfo? lastParameter = methodParameters.Length > 0 ? methodParameters[methodParameters.Length - 1] : null; + if (lastParameter is not null && + (lastParameter.GetCustomAttribute() is not null || + lastParameter.GetCustomAttribute() is not null)) + { + throw new NotSupportedException( + string.Format( + CultureInfo.InvariantCulture, + FrameworkMessages.DynamicDataInvalidMethodLayout, + method.DeclaringType?.FullName is { } typeFullName ? $"{typeFullName}.{method.Name}" : method.Name)); + } + + // Note: the method is static. + return method.Invoke(null, arguments.Length == 0 ? null : arguments); + } + + private static object? GetDataFromField(FieldInfo field) + { + if (!field.IsStatic) + { + throw new NotSupportedException( + string.Format( + CultureInfo.InvariantCulture, + FrameworkMessages.DynamicDataInvalidFieldLayout, + field.DeclaringType?.FullName is { } typeFullName ? $"{typeFullName}.{field.Name}" : field.Name)); + } + + // Note: the field is static. + return field.GetValue(null); } private static object? GetDataFromProperty(PropertyInfo property) @@ -115,16 +152,10 @@ private static bool TryGetData(object dataSource, [NotNullWhen(true)] out IEnume if (dataSource is IEnumerable enumerable and not string) { - List objects = new(); + List objects = []; foreach (object? entry in enumerable) { - if (entry is null) - { - data = null; - return false; - } - - objects.Add(new[] { entry }); + objects.Add([entry!]); } data = objects; @@ -135,6 +166,24 @@ private static bool TryGetData(object dataSource, [NotNullWhen(true)] out IEnume return false; } + private static FieldInfo? GetFieldConsideringInheritance(Type type, string fieldName) + { + // NOTE: Don't use GetRuntimeField. It considers inheritance only for instance fields. + Type? currentType = type; + while (currentType is not null) + { + FieldInfo? field = currentType.GetField(fieldName, DeclaredOnlyLookup); + if (field is not null) + { + return field; + } + + currentType = currentType.BaseType; + } + + return null; + } + private static PropertyInfo? GetPropertyConsideringInheritance(Type type, string propertyName) { // NOTE: Don't use GetRuntimeProperty. It considers inheritance only for instance properties. diff --git a/src/TestFramework/TestFramework/Attributes/Lifecycle/Cleanup/GlobalTestCleanupAttribute.cs b/src/TestFramework/TestFramework/Attributes/Lifecycle/Cleanup/GlobalTestCleanupAttribute.cs new file mode 100644 index 0000000000..fcb06aad7b --- /dev/null +++ b/src/TestFramework/TestFramework/Attributes/Lifecycle/Cleanup/GlobalTestCleanupAttribute.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.TestTools.UnitTesting; + +/// +/// A global test cleanup attribute that applies to every test method in the assembly. +/// The method to which this attribute is applied must be public, static, non-generic, has a single parameter of type TestContext, and either returns void or a Task. +/// +/// +/// Multiple methods with this attribute in the assembly is allowed, but there is no guarantee of the order in which they will be executed. +/// +[AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)] +public sealed class GlobalTestCleanupAttribute : Attribute; diff --git a/src/TestFramework/TestFramework/Attributes/Lifecycle/Initialization/GlobalTestInitializeAttribute.cs b/src/TestFramework/TestFramework/Attributes/Lifecycle/Initialization/GlobalTestInitializeAttribute.cs new file mode 100644 index 0000000000..ae7b0be236 --- /dev/null +++ b/src/TestFramework/TestFramework/Attributes/Lifecycle/Initialization/GlobalTestInitializeAttribute.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.TestTools.UnitTesting; + +/// +/// A global test initialize attribute that applies to every test method in the assembly. +/// The method to which this attribute is applied must be public, static, non-generic, has a single parameter of type TestContext, and either returns void or a Task. +/// +/// +/// Multiple methods with this attribute in the assembly is allowed, but there is no guarantee of the order in which they will be executed. In addition, TimeoutAttribute isn't supported on methods with this attribute. +/// +[AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)] +public sealed class GlobalTestInitializeAttribute : Attribute; diff --git a/src/TestFramework/TestFramework/Attributes/TestMethod/CIConditionAttribute.cs b/src/TestFramework/TestFramework/Attributes/TestMethod/CIConditionAttribute.cs new file mode 100644 index 0000000000..e4ae082e85 --- /dev/null +++ b/src/TestFramework/TestFramework/Attributes/TestMethod/CIConditionAttribute.cs @@ -0,0 +1,133 @@ +// 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.TestTools.UnitTesting; + +/// +/// This attribute is used to conditionally control whether a test class or a test method will run or be ignored based on whether the test is running in a CI environment. +/// +/// +/// This attribute isn't inherited. Applying it to a base class will not affect derived classes. +/// +[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = false, AllowMultiple = false)] +public sealed class CIConditionAttribute : ConditionBaseAttribute +{ + private readonly IEnvironment _environment; + + /// + /// Initializes a new instance of the class. + /// + /// Decides whether the test should be included or excluded in CI environments. + public CIConditionAttribute(ConditionMode mode) + : this(mode, new EnvironmentWrapper()) + { + } + + internal CIConditionAttribute(ConditionMode mode, IEnvironment environment) + : base(mode) + { + _environment = environment; + IgnoreMessage = mode == ConditionMode.Include + ? "Test is only supported in CI environments" + : "Test is not supported in CI environments"; + } + + /// + /// Gets a value indicating whether the test method or test class should run. + /// + public override bool ShouldRun => IsCIEnvironment(); + + /// + public override string? IgnoreMessage { get; set; } + + /// + /// Gets the group name for this attribute. + /// + public override string GroupName => nameof(CIConditionAttribute); + + // CI Detection logic based on 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 + private bool IsCIEnvironment() + { + // Systems that provide boolean values only, so we can simply parse and check for true + 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. + "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 + string[][] allNotNullVariables = + [ + // 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 + 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" + ]; + + foreach (string booleanVariable in booleanVariables) + { + if (bool.TryParse(_environment.GetEnvironmentVariable(booleanVariable), out bool envVar) && envVar) + { + return true; + } + } + + foreach (string[] variables in allNotNullVariables) + { + bool allVariablesPresent = true; + foreach (string variable in variables) + { + if (string.IsNullOrEmpty(_environment.GetEnvironmentVariable(variable))) + { + allVariablesPresent = false; + break; + } + } + + if (allVariablesPresent) + { + return true; + } + } + + foreach (string variable in ifNonNullVariables) + { + if (!string.IsNullOrEmpty(_environment.GetEnvironmentVariable(variable))) + { + return true; + } + } + + return false; + } +} diff --git a/src/TestFramework/TestFramework/Attributes/TestMethod/ConditionBaseAttribute.cs b/src/TestFramework/TestFramework/Attributes/TestMethod/ConditionBaseAttribute.cs index 7a5d8168fd..ed94a814d3 100644 --- a/src/TestFramework/TestFramework/Attributes/TestMethod/ConditionBaseAttribute.cs +++ b/src/TestFramework/TestFramework/Attributes/TestMethod/ConditionBaseAttribute.cs @@ -19,13 +19,16 @@ public abstract class ConditionBaseAttribute : Attribute protected ConditionBaseAttribute(ConditionMode mode) => Mode = mode; - internal ConditionMode Mode { get; } + /// + /// Gets the condition mode. + /// + public ConditionMode Mode { get; } /// - /// Gets the ignore message (in case returns ) indicating + /// Gets or sets the ignore message (in case returns ) indicating /// the reason for ignoring the test method or test class. /// - public abstract string? IgnoreMessage { get; } + public virtual string? IgnoreMessage { get; set; } /// /// Gets the group name for this attribute. This is relevant when multiple diff --git a/src/TestFramework/TestFramework/Attributes/TestMethod/CssIterationAttribute.cs b/src/TestFramework/TestFramework/Attributes/TestMethod/CssIterationAttribute.cs index a1c070d4a1..54f0a0d610 100644 --- a/src/TestFramework/TestFramework/Attributes/TestMethod/CssIterationAttribute.cs +++ b/src/TestFramework/TestFramework/Attributes/TestMethod/CssIterationAttribute.cs @@ -7,6 +7,11 @@ namespace Microsoft.VisualStudio.TestTools.UnitTesting; /// CSS Iteration URI. /// [AttributeUsage(AttributeTargets.Method)] +#if NET6_0_OR_GREATER +[Obsolete(FrameworkConstants.PublicTypeObsoleteMessage, DiagnosticId = "MSTESTOBS")] +#else +[Obsolete(FrameworkConstants.PublicTypeObsoleteMessage)] +#endif public sealed class CssIterationAttribute : Attribute { /// diff --git a/src/TestFramework/TestFramework/Attributes/TestMethod/CssProjectStructureAttribute.cs b/src/TestFramework/TestFramework/Attributes/TestMethod/CssProjectStructureAttribute.cs index eb3a354b45..e88074fde4 100644 --- a/src/TestFramework/TestFramework/Attributes/TestMethod/CssProjectStructureAttribute.cs +++ b/src/TestFramework/TestFramework/Attributes/TestMethod/CssProjectStructureAttribute.cs @@ -7,6 +7,11 @@ namespace Microsoft.VisualStudio.TestTools.UnitTesting; /// CSS Project Structure URI. /// [AttributeUsage(AttributeTargets.Method)] +#if NET6_0_OR_GREATER +[Obsolete(FrameworkConstants.PublicTypeObsoleteMessage, DiagnosticId = "MSTESTOBS")] +#else +[Obsolete(FrameworkConstants.PublicTypeObsoleteMessage)] +#endif public sealed class CssProjectStructureAttribute : Attribute { /// diff --git a/src/TestFramework/TestFramework/Attributes/TestMethod/DescriptionAttribute.cs b/src/TestFramework/TestFramework/Attributes/TestMethod/DescriptionAttribute.cs index 7917128c42..60fa6e5efb 100644 --- a/src/TestFramework/TestFramework/Attributes/TestMethod/DescriptionAttribute.cs +++ b/src/TestFramework/TestFramework/Attributes/TestMethod/DescriptionAttribute.cs @@ -7,16 +7,19 @@ namespace Microsoft.VisualStudio.TestTools.UnitTesting; /// Description of the test. /// [AttributeUsage(AttributeTargets.Method)] -public sealed class DescriptionAttribute : Attribute +public sealed class DescriptionAttribute : TestPropertyAttribute { /// /// Initializes a new instance of the class to describe a test. /// /// The description. - public DescriptionAttribute(string? description) => Description = description; + public DescriptionAttribute(string? description) + : base("Description", description ?? string.Empty) + { + } /// /// Gets the description of a test. /// - public string? Description { get; } + public string? Description => Value; } diff --git a/src/TestFramework/TestFramework/Attributes/TestMethod/IgnoreAttribute.cs b/src/TestFramework/TestFramework/Attributes/TestMethod/IgnoreAttribute.cs index bab35c458d..122ae1fccf 100644 --- a/src/TestFramework/TestFramework/Attributes/TestMethod/IgnoreAttribute.cs +++ b/src/TestFramework/TestFramework/Attributes/TestMethod/IgnoreAttribute.cs @@ -30,10 +30,8 @@ public IgnoreAttribute(string? message) : base(ConditionMode.Include) => IgnoreMessage = message; - /// - /// Gets the ignore message indicating the reason for ignoring the test method or test class. - /// - public override string? IgnoreMessage { get; } + /// + public override string? IgnoreMessage { get; set; } /// public override bool ShouldRun => false; diff --git a/src/TestFramework/TestFramework/Attributes/TestMethod/OSConditionAttribute.cs b/src/TestFramework/TestFramework/Attributes/TestMethod/OSConditionAttribute.cs index 49906ede5d..63a8e6db00 100644 --- a/src/TestFramework/TestFramework/Attributes/TestMethod/OSConditionAttribute.cs +++ b/src/TestFramework/TestFramework/Attributes/TestMethod/OSConditionAttribute.cs @@ -32,7 +32,9 @@ public OSConditionAttribute(ConditionMode mode, OperatingSystems operatingSystem : base(mode) { _operatingSystems = operatingSystems; - IgnoreMessage = $"Test is only supported on {operatingSystems}"; + IgnoreMessage = mode == ConditionMode.Include + ? $"Test is only supported on {operatingSystems}" + : $"Test is not supported on {operatingSystems}"; } /// @@ -77,10 +79,8 @@ public override bool ShouldRun } #endif - /// - /// Gets the ignore message (in case returns ). - /// - public override string? IgnoreMessage { get; } + /// + public override string? IgnoreMessage { get; set; } /// /// Gets the group name for this attribute. diff --git a/src/TestFramework/TestFramework/Attributes/TestMethod/OwnerAttribute.cs b/src/TestFramework/TestFramework/Attributes/TestMethod/OwnerAttribute.cs index 5f33b2ed23..72526ac379 100644 --- a/src/TestFramework/TestFramework/Attributes/TestMethod/OwnerAttribute.cs +++ b/src/TestFramework/TestFramework/Attributes/TestMethod/OwnerAttribute.cs @@ -7,7 +7,7 @@ namespace Microsoft.VisualStudio.TestTools.UnitTesting; /// Test Owner. /// [AttributeUsage(AttributeTargets.Method)] -public sealed class OwnerAttribute : Attribute +public sealed class OwnerAttribute : TestPropertyAttribute { /// /// Initializes a new instance of the class. @@ -15,10 +15,13 @@ public sealed class OwnerAttribute : Attribute /// /// The owner. /// - public OwnerAttribute(string? owner) => Owner = owner; + public OwnerAttribute(string? owner) + : base("Owner", owner ?? string.Empty) + { + } /// /// Gets the owner. /// - public string? Owner { get; } + public string? Owner => Value; } diff --git a/src/TestFramework/TestFramework/Attributes/TestMethod/PriorityAttribute.cs b/src/TestFramework/TestFramework/Attributes/TestMethod/PriorityAttribute.cs index eaf0ae7532..ab0da4cddf 100644 --- a/src/TestFramework/TestFramework/Attributes/TestMethod/PriorityAttribute.cs +++ b/src/TestFramework/TestFramework/Attributes/TestMethod/PriorityAttribute.cs @@ -7,7 +7,7 @@ namespace Microsoft.VisualStudio.TestTools.UnitTesting; /// Priority attribute; used to specify the priority of a unit test. /// [AttributeUsage(AttributeTargets.Method)] -public sealed class PriorityAttribute : Attribute +public sealed class PriorityAttribute : TestPropertyAttribute { /// /// Initializes a new instance of the class. @@ -15,7 +15,8 @@ public sealed class PriorityAttribute : Attribute /// /// The priority. /// - public PriorityAttribute(int priority) => Priority = priority; + public PriorityAttribute(int priority) + : base("Priority", priority.ToString(CultureInfo.InvariantCulture)) => Priority = priority; /// /// Gets the priority. diff --git a/src/TestFramework/TestFramework/Attributes/TestMethod/RetryAttribute.cs b/src/TestFramework/TestFramework/Attributes/TestMethod/RetryAttribute.cs index 0ee55befc5..ce0ef338d7 100644 --- a/src/TestFramework/TestFramework/Attributes/TestMethod/RetryAttribute.cs +++ b/src/TestFramework/TestFramework/Attributes/TestMethod/RetryAttribute.cs @@ -15,10 +15,12 @@ public sealed class RetryAttribute : RetryBaseAttribute /// The maximum number of retry attempts. This must be greater than or equal to 1. public RetryAttribute(int maxRetryAttempts) { +#pragma warning disable CA1512 // Use ArgumentOutOfRangeException throw helper if (maxRetryAttempts < 1) { throw new ArgumentOutOfRangeException(nameof(maxRetryAttempts)); } +#pragma warning disable CA1512 // Use ArgumentOutOfRangeException throw helper MaxRetryAttempts = maxRetryAttempts; } @@ -41,7 +43,7 @@ public RetryAttribute(int maxRetryAttempts) /// public DelayBackoffType BackoffType { - get => field; + get; set { if (value is < DelayBackoffType.Constant or > DelayBackoffType.Exponential) @@ -71,13 +73,13 @@ protected internal override async Task ExecuteAsync(RetryContext re for (int i = 0; i < MaxRetryAttempts; i++) { // The caller already executed the test once. So we need to do the delay here. - await Task.Delay(currentDelay); + await Task.Delay(currentDelay).ConfigureAwait(false); if (BackoffType == DelayBackoffType.Exponential) { currentDelay *= 2; } - TestResult[] testResults = await retryContext.ExecuteTaskGetter(); + TestResult[] testResults = await retryContext.ExecuteTaskGetter().ConfigureAwait(false); result.AddResult(testResults); if (IsAcceptableResultForRetry(testResults)) { diff --git a/src/TestFramework/TestFramework/Attributes/TestMethod/RetryResult.cs b/src/TestFramework/TestFramework/Attributes/TestMethod/RetryResult.cs index 60dc976cf9..855893fc95 100644 --- a/src/TestFramework/TestFramework/Attributes/TestMethod/RetryResult.cs +++ b/src/TestFramework/TestFramework/Attributes/TestMethod/RetryResult.cs @@ -9,7 +9,7 @@ namespace Microsoft.VisualStudio.TestTools.UnitTesting; [Experimental("MSTESTEXP", UrlFormat = "https://aka.ms/mstest/diagnostics#{0}")] public sealed class RetryResult { - private readonly List _testResults = new(); + private readonly List _testResults = []; /// /// Adds a set of test results to the retry result. diff --git a/src/TestFramework/TestFramework/Attributes/TestMethod/STATestMethodAttribute.cs b/src/TestFramework/TestFramework/Attributes/TestMethod/STATestMethodAttribute.cs index a0cecc24aa..3f317a3eac 100644 --- a/src/TestFramework/TestFramework/Attributes/TestMethod/STATestMethodAttribute.cs +++ b/src/TestFramework/TestFramework/Attributes/TestMethod/STATestMethodAttribute.cs @@ -9,7 +9,7 @@ namespace Microsoft.VisualStudio.TestTools.UnitTesting; [AttributeUsage(AttributeTargets.Method)] public class STATestMethodAttribute : TestMethodAttribute { - private protected override bool UseAsync => true; + private protected override bool UseAsync => GetType() == typeof(STATestMethodAttribute); /// /// Initializes a new instance of the class. diff --git a/src/TestFramework/TestFramework/Attributes/TestMethod/TestMethodAttribute.cs b/src/TestFramework/TestFramework/Attributes/TestMethod/TestMethodAttribute.cs index 75f7455b3b..aa76cac719 100644 --- a/src/TestFramework/TestFramework/Attributes/TestMethod/TestMethodAttribute.cs +++ b/src/TestFramework/TestFramework/Attributes/TestMethod/TestMethodAttribute.cs @@ -67,9 +67,8 @@ public TestMethodAttribute(string? displayName) /// The test method to execute. /// An array of TestResult objects that represent the outcome(s) of the test. /// Extensions can override this method to customize running a TestMethod. - // TODO: Double check whether this breaks async local propagation between test init, test, test cleanup internal virtual async Task ExecuteAsync(ITestMethod testMethod) => UseAsync - ? [await testMethod.InvokeAsync(null)] + ? [await testMethod.InvokeAsync(null).ConfigureAwait(false)] : Execute(testMethod); } diff --git a/src/TestFramework/TestFramework/Attributes/TestMethod/TestTimeout.cs b/src/TestFramework/TestFramework/Attributes/TestMethod/TestTimeout.cs index 3c5aab0c09..629d58628c 100644 --- a/src/TestFramework/TestFramework/Attributes/TestMethod/TestTimeout.cs +++ b/src/TestFramework/TestFramework/Attributes/TestMethod/TestTimeout.cs @@ -8,6 +8,11 @@ namespace Microsoft.VisualStudio.TestTools.UnitTesting; /// The type of the enumeration must match. /// [SuppressMessage("Microsoft.Design", "CA1008:EnumsShouldHaveZeroValue", Justification = "Compat reasons")] +#if NET6_0_OR_GREATER +[Obsolete(FrameworkConstants.TestTimeoutAttributeObsoleteMessage, error: false, DiagnosticId = "MSTESTOBS")] +#else +[Obsolete(FrameworkConstants.TestTimeoutAttributeObsoleteMessage, error: false)] +#endif public enum TestTimeout { /// diff --git a/src/TestFramework/TestFramework/Attributes/TestMethod/TimeoutAttribute.cs b/src/TestFramework/TestFramework/Attributes/TestMethod/TimeoutAttribute.cs index b57e1941b2..a23a6ba47e 100644 --- a/src/TestFramework/TestFramework/Attributes/TestMethod/TimeoutAttribute.cs +++ b/src/TestFramework/TestFramework/Attributes/TestMethod/TimeoutAttribute.cs @@ -23,6 +23,11 @@ public sealed class TimeoutAttribute : Attribute /// /// The timeout. /// +#if NET6_0_OR_GREATER + [Obsolete(FrameworkConstants.TestTimeoutAttributeObsoleteMessage, error: false, DiagnosticId = "MSTESTOBS")] +#else + [Obsolete(FrameworkConstants.TestTimeoutAttributeObsoleteMessage, error: false)] +#endif public TimeoutAttribute(TestTimeout timeout) => Timeout = (int)timeout; /// diff --git a/src/TestFramework/TestFramework/Constants.cs b/src/TestFramework/TestFramework/Constants.cs deleted file mode 100644 index 6973e5008c..0000000000 --- a/src/TestFramework/TestFramework/Constants.cs +++ /dev/null @@ -1,12 +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.VisualStudio.TestTools.UnitTesting; - -/// -/// Constants used throughout. -/// -internal static class Constants -{ - internal const string PublicTypeObsoleteMessage = "We will remove or hide this type starting with v4. If you are using this type, reach out to our team on https://github.com/microsoft/testfx."; -} diff --git a/src/TestFramework/TestFramework/Exceptions/AssertFailedException.cs b/src/TestFramework/TestFramework/Exceptions/AssertFailedException.cs index bff933dbd6..2bbd14c132 100644 --- a/src/TestFramework/TestFramework/Exceptions/AssertFailedException.cs +++ b/src/TestFramework/TestFramework/Exceptions/AssertFailedException.cs @@ -45,7 +45,7 @@ public AssertFailedException() /// Serialization info. /// Streaming context. #if NET8_0_OR_GREATER - [Obsolete(DiagnosticId = "SYSLIB0051")] + [Obsolete("Legacy serialization support is deprecated since .NET 8", DiagnosticId = "SYSLIB0051")] #endif [EditorBrowsable(EditorBrowsableState.Never)] protected AssertFailedException(SerializationInfo info, StreamingContext context) diff --git a/src/TestFramework/TestFramework/Exceptions/AssertInconclusiveException.cs b/src/TestFramework/TestFramework/Exceptions/AssertInconclusiveException.cs index b6734d5e38..277ce83509 100644 --- a/src/TestFramework/TestFramework/Exceptions/AssertInconclusiveException.cs +++ b/src/TestFramework/TestFramework/Exceptions/AssertInconclusiveException.cs @@ -45,7 +45,7 @@ public AssertInconclusiveException() /// Serialization info. /// Streaming context. #if NET8_0_OR_GREATER - [Obsolete(DiagnosticId = "SYSLIB0051")] + [Obsolete("Legacy serialization support is deprecated since .NET 8", DiagnosticId = "SYSLIB0051")] #endif [EditorBrowsable(EditorBrowsableState.Never)] protected AssertInconclusiveException(SerializationInfo info, StreamingContext context) diff --git a/src/TestFramework/TestFramework/Exceptions/InternalTestFailureException.cs b/src/TestFramework/TestFramework/Exceptions/InternalTestFailureException.cs index 78f3de327f..519eb8b651 100644 --- a/src/TestFramework/TestFramework/Exceptions/InternalTestFailureException.cs +++ b/src/TestFramework/TestFramework/Exceptions/InternalTestFailureException.cs @@ -14,9 +14,9 @@ namespace Microsoft.VisualStudio.TestTools.UnitTesting; /// For all practical purposes either use AssertFailedException/AssertInconclusiveException. /// #if NET6_0_OR_GREATER -[Obsolete(Constants.PublicTypeObsoleteMessage, DiagnosticId = "MSTESTOBS")] +[Obsolete(FrameworkConstants.PublicTypeObsoleteMessage, DiagnosticId = "MSTESTOBS")] #else -[Obsolete(Constants.PublicTypeObsoleteMessage)] +[Obsolete(FrameworkConstants.PublicTypeObsoleteMessage)] #endif [Serializable] public class InternalTestFailureException : UnitTestAssertException @@ -54,7 +54,7 @@ public InternalTestFailureException() /// Serialization info. /// Streaming context. #if NET8_0_OR_GREATER - [Obsolete(DiagnosticId = "SYSLIB0051")] + [Obsolete("Legacy serialization support is deprecated since .NET 8", DiagnosticId = "SYSLIB0051")] #endif [EditorBrowsable(EditorBrowsableState.Never)] protected InternalTestFailureException(SerializationInfo info, StreamingContext context) diff --git a/src/TestFramework/TestFramework/Exceptions/UnitTestAssertException.cs b/src/TestFramework/TestFramework/Exceptions/UnitTestAssertException.cs index 37e2a86510..85978e9597 100644 --- a/src/TestFramework/TestFramework/Exceptions/UnitTestAssertException.cs +++ b/src/TestFramework/TestFramework/Exceptions/UnitTestAssertException.cs @@ -44,7 +44,7 @@ protected UnitTestAssertException(string msg) /// Serialization info. /// Streaming context. #if NET8_0_OR_GREATER - [Obsolete(DiagnosticId = "SYSLIB0051")] + [Obsolete("Legacy serialization support is deprecated since .NET 8", DiagnosticId = "SYSLIB0051")] #endif [EditorBrowsable(EditorBrowsableState.Never)] protected UnitTestAssertException(SerializationInfo info, StreamingContext context) diff --git a/src/TestFramework/TestFramework/FrameworkConstants.cs b/src/TestFramework/TestFramework/FrameworkConstants.cs new file mode 100644 index 0000000000..f0bc8da76e --- /dev/null +++ b/src/TestFramework/TestFramework/FrameworkConstants.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. + +namespace Microsoft.VisualStudio.TestTools.UnitTesting; + +/// +/// Constants used throughout. +/// +internal static class FrameworkConstants +{ + internal const string PublicTypeObsoleteMessage = "We will remove or hide this type starting with v4. If you are using this type, reach out to our team on https://github.com/microsoft/testfx."; + internal const string ThatPropertyObsoleteMessage = "The 'That' property is obsolete and will be removed in a future version. Use the 'Instance' property instead."; + internal const string DoNotUseAssertEquals = "Assert.Equals should not be used for Assertions. Please use Assert.AreEqual & overloads instead."; + internal const string DoNotUseAssertReferenceEquals = "Assert.ReferenceEquals should not be used for Assertions. Please use Assert.AreSame & overloads instead."; + internal const string DoNotUseStringAssertEquals = "StringAssert.Equals should not be used for Assertions. Please use StringAssert methods or Assert.AreEqual & overloads instead."; + internal const string DoNotUseStringAssertReferenceEquals = "StringAssert.ReferenceEquals should not be used for Assertions. Please use StringAssert methods or Assert.AreSame & overloads instead."; + internal const string DoNotUseCollectionAssertEquals = "CollectionAssert.Equals should not be used for Assertions. Please use CollectionAssert.AreEqual & overloads instead."; + internal const string DoNotUseCollectionAssertReferenceEquals = "CollectionAssert.ReferenceEquals should not be used for Assertions. Please use CollectionAssert methods or Assert.AreSame & overloads instead."; + internal const string TestTimeoutAttributeObsoleteMessage = "The 'TestTimeout' attribute is obsolete and will be removed in v4. Consider removing the 'Timeout' attribute or use the 'Timeout' attribute with the 'int.MaxValue' for infinite timeout."; +} diff --git a/src/TestFramework/TestFramework/Friends.cs b/src/TestFramework/TestFramework/Friends.cs deleted file mode 100644 index ebbc3313b8..0000000000 --- a/src/TestFramework/TestFramework/Friends.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. - -[assembly: InternalsVisibleTo("Microsoft.VisualStudio.TestPlatform.TestFramework.UnitTests, PublicKey=002400000480000094000000060200000024000052534131000400000100010007d1fa57c4aed9f0a32e84aa0faefd0de9e8fd6aec8f87fb03766c834c99921eb23be79ad9d5dcc1dd9ad236132102900b723cf980957fc4e177108fc607774f29e8320e92ea05ece4e821c0a5efe8f1645c4c0c93c1ab99285d622caa652c1dfad63d745d6f2de5f17e5eaf0fc4963d261c8a12436518206dc093344d5ad293")] -[assembly: InternalsVisibleTo("Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions, PublicKey=002400000480000094000000060200000024000052534131000400000100010007d1fa57c4aed9f0a32e84aa0faefd0de9e8fd6aec8f87fb03766c834c99921eb23be79ad9d5dcc1dd9ad236132102900b723cf980957fc4e177108fc607774f29e8320e92ea05ece4e821c0a5efe8f1645c4c0c93c1ab99285d622caa652c1dfad63d745d6f2de5f17e5eaf0fc4963d261c8a12436518206dc093344d5ad293")] -[assembly: InternalsVisibleTo("Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter, PublicKey=002400000480000094000000060200000024000052534131000400000100010007d1fa57c4aed9f0a32e84aa0faefd0de9e8fd6aec8f87fb03766c834c99921eb23be79ad9d5dcc1dd9ad236132102900b723cf980957fc4e177108fc607774f29e8320e92ea05ece4e821c0a5efe8f1645c4c0c93c1ab99285d622caa652c1dfad63d745d6f2de5f17e5eaf0fc4963d261c8a12436518206dc093344d5ad293")] -[assembly: InternalsVisibleTo("Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices, PublicKey=002400000480000094000000060200000024000052534131000400000100010007d1fa57c4aed9f0a32e84aa0faefd0de9e8fd6aec8f87fb03766c834c99921eb23be79ad9d5dcc1dd9ad236132102900b723cf980957fc4e177108fc607774f29e8320e92ea05ece4e821c0a5efe8f1645c4c0c93c1ab99285d622caa652c1dfad63d745d6f2de5f17e5eaf0fc4963d261c8a12436518206dc093344d5ad293")] -[assembly: InternalsVisibleTo("MSTest.TestAnywhereAdapter, PublicKey=002400000480000094000000060200000024000052534131000400000100010007d1fa57c4aed9f0a32e84aa0faefd0de9e8fd6aec8f87fb03766c834c99921eb23be79ad9d5dcc1dd9ad236132102900b723cf980957fc4e177108fc607774f29e8320e92ea05ece4e821c0a5efe8f1645c4c0c93c1ab99285d622caa652c1dfad63d745d6f2de5f17e5eaf0fc4963d261c8a12436518206dc093344d5ad293")] diff --git a/src/TestFramework/TestFramework/GenericParameterHelper.cs b/src/TestFramework/TestFramework/GenericParameterHelper.cs index 2012f7c748..238cbe201d 100644 --- a/src/TestFramework/TestFramework/GenericParameterHelper.cs +++ b/src/TestFramework/TestFramework/GenericParameterHelper.cs @@ -16,9 +16,9 @@ namespace Microsoft.VisualStudio.TestTools.UnitTesting; [SuppressMessage("Microsoft.Design", "CA1036:OverrideMethodsOnComparableTypes", Justification = "Compat reasons.")] [SuppressMessage("Design", "CA1010:Generic interface should also be implemented", Justification = "Part of the public API")] #if NET6_0_OR_GREATER -[Obsolete(Constants.PublicTypeObsoleteMessage, DiagnosticId = "MSTESTOBS")] +[Obsolete(FrameworkConstants.PublicTypeObsoleteMessage, DiagnosticId = "MSTESTOBS")] #else -[Obsolete(Constants.PublicTypeObsoleteMessage)] +[Obsolete(FrameworkConstants.PublicTypeObsoleteMessage)] #endif // GenericParameterHelper in full CLR version also implements ICloneable, but we don't have ICloneable in core CLR public class GenericParameterHelper : IComparable, IEnumerable diff --git a/src/TestFramework/TestFramework/ITestDataRow.cs b/src/TestFramework/TestFramework/ITestDataRow.cs index 2150fa1c96..ed8ea6c612 100644 --- a/src/TestFramework/TestFramework/ITestDataRow.cs +++ b/src/TestFramework/TestFramework/ITestDataRow.cs @@ -10,4 +10,6 @@ internal interface ITestDataRow string? IgnoreMessage { get; } string? DisplayName { get; } + + IList? TestCategories { get; } } diff --git a/src/TestFramework/TestFramework/Internal/ApplicationStateGuard.cs b/src/TestFramework/TestFramework/Internal/ApplicationStateGuard.cs new file mode 100644 index 0000000000..bfa64b6765 --- /dev/null +++ b/src/TestFramework/TestFramework/Internal/ApplicationStateGuard.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.VisualStudio.TestTools.UnitTesting; + +internal static class ApplicationStateGuard +{ + public static UnreachableException Unreachable([CallerFilePath] string? path = null, [CallerLineNumber] int line = 0) + => new($"This program location is thought to be unreachable. File='{path}' Line={line}"); +} diff --git a/src/TestFramework/TestFramework/Internal/DebugEx.cs b/src/TestFramework/TestFramework/Internal/DebugEx.cs index 77b9d8fd02..9fdce6f146 100644 --- a/src/TestFramework/TestFramework/Internal/DebugEx.cs +++ b/src/TestFramework/TestFramework/Internal/DebugEx.cs @@ -17,7 +17,9 @@ public static void Assert([DoesNotReturnIf(false)] bool b, string message) // ends up causing the job to timeout. We use FailFast instead. // FailFast is better than throwing an exception to avoid anyone // catching an exception and masking an assert failure. +#pragma warning disable CA2201 // Do not raise reserved exception types var ex = new Exception($"Debug.Assert failed: {message}"); +#pragma warning restore CA2201 // Do not raise reserved exception types Environment.FailFast(ex.Message, ex); } } diff --git a/src/TestFramework/TestFramework/Internal/IEnvironment.cs b/src/TestFramework/TestFramework/Internal/IEnvironment.cs new file mode 100644 index 0000000000..6624207d3e --- /dev/null +++ b/src/TestFramework/TestFramework/Internal/IEnvironment.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.TestTools.UnitTesting; + +/// +/// Interface to abstract environment related information. +/// +internal interface IEnvironment +{ + /// + /// Gets the machine name. + /// + string MachineName { get; } + + /// + void SetEnvironmentVariable(string variable, string? value); + + /// + string? GetEnvironmentVariable(string name); +} + +internal sealed class EnvironmentWrapper : IEnvironment +{ + public string MachineName => Environment.MachineName; + + public string? GetEnvironmentVariable(string name) => Environment.GetEnvironmentVariable(name); + + public void SetEnvironmentVariable(string variable, string? value) => Environment.SetEnvironmentVariable(variable, value); +} diff --git a/src/TestFramework/TestFramework/Internal/TestDataSourceUtilities.cs b/src/TestFramework/TestFramework/Internal/TestDataSourceUtilities.cs index 525f25fda2..d594f38ad4 100644 --- a/src/TestFramework/TestFramework/Internal/TestDataSourceUtilities.cs +++ b/src/TestFramework/TestFramework/Internal/TestDataSourceUtilities.cs @@ -29,7 +29,7 @@ internal static class TestDataSourceUtilities CultureInfo.CurrentCulture, FrameworkMessages.DataDrivenResultDisplayName, methodDisplayName, - string.Join(",", displayData.Select(x => GetHumanizedArguments(x, testIdGenerationStrategy)))); + string.Join(',', displayData.Select(x => GetHumanizedArguments(x, testIdGenerationStrategy)))); } /// @@ -65,6 +65,6 @@ internal static class TestDataSourceUtilities // We need to box the object here so that we can support value types IEnumerable boxedObjectEnumerable = ((IEnumerable)data).Cast(); IEnumerable elementStrings = boxedObjectEnumerable.Select(x => GetHumanizedArguments(x, testIdGenerationStrategy)); - return $"[{string.Join(",", elementStrings)}]"; + return $"[{string.Join(',', elementStrings)}]"; } } diff --git a/src/TestFramework/TestFramework/PublicAPI/PublicAPI.Shipped.txt b/src/TestFramework/TestFramework/PublicAPI/PublicAPI.Shipped.txt index 12691b823c..7954d442eb 100644 --- a/src/TestFramework/TestFramework/PublicAPI/PublicAPI.Shipped.txt +++ b/src/TestFramework/TestFramework/PublicAPI/PublicAPI.Shipped.txt @@ -2,12 +2,12 @@ [MSTESTEXP]abstract Microsoft.VisualStudio.TestTools.UnitTesting.RetryBaseAttribute.ExecuteAsync(Microsoft.VisualStudio.TestTools.UnitTesting.RetryContext retryContext) -> System.Threading.Tasks.Task! [MSTESTEXP]Microsoft.VisualStudio.TestTools.UnitTesting.RetryContext [MSTESTEXP]Microsoft.VisualStudio.TestTools.UnitTesting.RetryContext.ExecuteTaskGetter.get -> System.Func!>! +[MSTESTEXP]Microsoft.VisualStudio.TestTools.UnitTesting.RetryContext.FirstRunResults.get -> Microsoft.VisualStudio.TestTools.UnitTesting.TestResult![]! [MSTESTEXP]Microsoft.VisualStudio.TestTools.UnitTesting.RetryContext.RetryContext() -> void [MSTESTEXP]Microsoft.VisualStudio.TestTools.UnitTesting.RetryResult [MSTESTEXP]Microsoft.VisualStudio.TestTools.UnitTesting.RetryResult.AddResult(Microsoft.VisualStudio.TestTools.UnitTesting.TestResult![]! testResults) -> void [MSTESTEXP]Microsoft.VisualStudio.TestTools.UnitTesting.RetryResult.RetryResult() -> void abstract Microsoft.VisualStudio.TestTools.UnitTesting.ConditionBaseAttribute.GroupName.get -> string! -abstract Microsoft.VisualStudio.TestTools.UnitTesting.ConditionBaseAttribute.IgnoreMessage.get -> string? abstract Microsoft.VisualStudio.TestTools.UnitTesting.ConditionBaseAttribute.ShouldRun.get -> bool abstract Microsoft.VisualStudio.TestTools.UnitTesting.ExpectedExceptionBaseAttribute.Verify(System.Exception! exception) -> void abstract Microsoft.VisualStudio.TestTools.UnitTesting.TestCategoryBaseAttribute.TestCategories.get -> System.Collections.Generic.IList! @@ -217,6 +217,7 @@ Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertNonStrictThrowsInterpo Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertNonStrictThrowsInterpolatedStringHandler.AppendLiteral(string! value) -> void Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertNonStrictThrowsInterpolatedStringHandler.AssertNonStrictThrowsInterpolatedStringHandler() -> void Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertNonStrictThrowsInterpolatedStringHandler.AssertNonStrictThrowsInterpolatedStringHandler(int literalLength, int formattedCount, System.Action! action, out bool shouldAppend) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertNonStrictThrowsInterpolatedStringHandler.AssertNonStrictThrowsInterpolatedStringHandler(int literalLength, int formattedCount, System.Func! action, out bool shouldAppend) -> void Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertSingleInterpolatedStringHandler Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertSingleInterpolatedStringHandler.AppendFormatted(object? value, int alignment = 0, string? format = null) -> void Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertSingleInterpolatedStringHandler.AppendFormatted(string? value, int alignment = 0, string? format = null) -> void @@ -239,14 +240,19 @@ Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertThrowsExactlyInterpola Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertThrowsExactlyInterpolatedStringHandler.AppendLiteral(string! value) -> void Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertThrowsExactlyInterpolatedStringHandler.AssertThrowsExactlyInterpolatedStringHandler() -> void Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertThrowsExactlyInterpolatedStringHandler.AssertThrowsExactlyInterpolatedStringHandler(int literalLength, int formattedCount, System.Action! action, out bool shouldAppend) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertThrowsExactlyInterpolatedStringHandler.AssertThrowsExactlyInterpolatedStringHandler(int literalLength, int formattedCount, System.Func! action, out bool shouldAppend) -> void Microsoft.VisualStudio.TestTools.UnitTesting.AssertFailedException Microsoft.VisualStudio.TestTools.UnitTesting.AssertFailedException.AssertFailedException() -> void Microsoft.VisualStudio.TestTools.UnitTesting.AssertFailedException.AssertFailedException(string! msg, System.Exception! ex) -> void Microsoft.VisualStudio.TestTools.UnitTesting.AssertFailedException.AssertFailedException(string! msg) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.AssertFailedException.AssertFailedException(System.Runtime.Serialization.SerializationInfo! info, System.Runtime.Serialization.StreamingContext context) -> void Microsoft.VisualStudio.TestTools.UnitTesting.AssertInconclusiveException Microsoft.VisualStudio.TestTools.UnitTesting.AssertInconclusiveException.AssertInconclusiveException() -> void Microsoft.VisualStudio.TestTools.UnitTesting.AssertInconclusiveException.AssertInconclusiveException(string! msg, System.Exception! ex) -> void Microsoft.VisualStudio.TestTools.UnitTesting.AssertInconclusiveException.AssertInconclusiveException(string! msg) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.AssertInconclusiveException.AssertInconclusiveException(System.Runtime.Serialization.SerializationInfo! info, System.Runtime.Serialization.StreamingContext context) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.CIConditionAttribute +Microsoft.VisualStudio.TestTools.UnitTesting.CIConditionAttribute.CIConditionAttribute(Microsoft.VisualStudio.TestTools.UnitTesting.ConditionMode mode) -> void Microsoft.VisualStudio.TestTools.UnitTesting.ClassCleanupAttribute Microsoft.VisualStudio.TestTools.UnitTesting.ClassCleanupAttribute.ClassCleanupAttribute() -> void Microsoft.VisualStudio.TestTools.UnitTesting.ClassCleanupAttribute.ClassCleanupAttribute(Microsoft.VisualStudio.TestTools.UnitTesting.ClassCleanupBehavior cleanupBehavior) -> void @@ -268,6 +274,7 @@ Microsoft.VisualStudio.TestTools.UnitTesting.ClassInitializeAttribute.Inheritanc Microsoft.VisualStudio.TestTools.UnitTesting.CollectionAssert Microsoft.VisualStudio.TestTools.UnitTesting.ConditionBaseAttribute Microsoft.VisualStudio.TestTools.UnitTesting.ConditionBaseAttribute.ConditionBaseAttribute(Microsoft.VisualStudio.TestTools.UnitTesting.ConditionMode mode) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.ConditionBaseAttribute.Mode.get -> Microsoft.VisualStudio.TestTools.UnitTesting.ConditionMode Microsoft.VisualStudio.TestTools.UnitTesting.ConditionMode Microsoft.VisualStudio.TestTools.UnitTesting.ConditionMode.Exclude = 1 -> Microsoft.VisualStudio.TestTools.UnitTesting.ConditionMode Microsoft.VisualStudio.TestTools.UnitTesting.ConditionMode.Include = 0 -> Microsoft.VisualStudio.TestTools.UnitTesting.ConditionMode @@ -317,7 +324,9 @@ Microsoft.VisualStudio.TestTools.UnitTesting.DoNotParallelizeAttribute Microsoft.VisualStudio.TestTools.UnitTesting.DoNotParallelizeAttribute.DoNotParallelizeAttribute() -> void Microsoft.VisualStudio.TestTools.UnitTesting.DynamicDataAttribute Microsoft.VisualStudio.TestTools.UnitTesting.DynamicDataAttribute.DynamicDataAttribute(string! dynamicDataSourceName, Microsoft.VisualStudio.TestTools.UnitTesting.DynamicDataSourceType dynamicDataSourceType) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.DynamicDataAttribute.DynamicDataAttribute(string! dynamicDataSourceName, params object?[]! dynamicDataSourceArguments) -> void Microsoft.VisualStudio.TestTools.UnitTesting.DynamicDataAttribute.DynamicDataAttribute(string! dynamicDataSourceName, System.Type! dynamicDataDeclaringType, Microsoft.VisualStudio.TestTools.UnitTesting.DynamicDataSourceType dynamicDataSourceType) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.DynamicDataAttribute.DynamicDataAttribute(string! dynamicDataSourceName, System.Type! dynamicDataDeclaringType, params object?[]! dynamicDataSourceArguments) -> void Microsoft.VisualStudio.TestTools.UnitTesting.DynamicDataAttribute.DynamicDataAttribute(string! dynamicDataSourceName, System.Type! dynamicDataDeclaringType) -> void Microsoft.VisualStudio.TestTools.UnitTesting.DynamicDataAttribute.DynamicDataAttribute(string! dynamicDataSourceName) -> void Microsoft.VisualStudio.TestTools.UnitTesting.DynamicDataAttribute.DynamicDataDisplayName.get -> string? @@ -359,6 +368,10 @@ Microsoft.VisualStudio.TestTools.UnitTesting.GenericParameterHelper.GetEnumerato Microsoft.VisualStudio.TestTools.UnitTesting.GitHubWorkItemAttribute Microsoft.VisualStudio.TestTools.UnitTesting.GitHubWorkItemAttribute.GitHubWorkItemAttribute(string! url) -> void Microsoft.VisualStudio.TestTools.UnitTesting.GitHubWorkItemAttribute.Url.get -> string! +Microsoft.VisualStudio.TestTools.UnitTesting.GlobalTestCleanupAttribute +Microsoft.VisualStudio.TestTools.UnitTesting.GlobalTestCleanupAttribute.GlobalTestCleanupAttribute() -> void +Microsoft.VisualStudio.TestTools.UnitTesting.GlobalTestInitializeAttribute +Microsoft.VisualStudio.TestTools.UnitTesting.GlobalTestInitializeAttribute.GlobalTestInitializeAttribute() -> void Microsoft.VisualStudio.TestTools.UnitTesting.IgnoreAttribute Microsoft.VisualStudio.TestTools.UnitTesting.IgnoreAttribute.IgnoreAttribute() -> void Microsoft.VisualStudio.TestTools.UnitTesting.IgnoreAttribute.IgnoreAttribute(string? message) -> void @@ -369,6 +382,7 @@ Microsoft.VisualStudio.TestTools.UnitTesting.InternalTestFailureException Microsoft.VisualStudio.TestTools.UnitTesting.InternalTestFailureException.InternalTestFailureException() -> void Microsoft.VisualStudio.TestTools.UnitTesting.InternalTestFailureException.InternalTestFailureException(string! msg, System.Exception! ex) -> void Microsoft.VisualStudio.TestTools.UnitTesting.InternalTestFailureException.InternalTestFailureException(string! msg) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.InternalTestFailureException.InternalTestFailureException(System.Runtime.Serialization.SerializationInfo! info, System.Runtime.Serialization.StreamingContext context) -> void Microsoft.VisualStudio.TestTools.UnitTesting.ITestDataSource Microsoft.VisualStudio.TestTools.UnitTesting.ITestDataSource.GetData(System.Reflection.MethodInfo! methodInfo) -> System.Collections.Generic.IEnumerable! Microsoft.VisualStudio.TestTools.UnitTesting.ITestDataSource.GetDisplayName(System.Reflection.MethodInfo! methodInfo, object?[]? data) -> string? @@ -382,6 +396,7 @@ Microsoft.VisualStudio.TestTools.UnitTesting.ITestMethod.Arguments.get -> object Microsoft.VisualStudio.TestTools.UnitTesting.ITestMethod.GetAllAttributes(bool inherit) -> System.Attribute![]? Microsoft.VisualStudio.TestTools.UnitTesting.ITestMethod.GetAttributes(bool inherit) -> TAttributeType![]! Microsoft.VisualStudio.TestTools.UnitTesting.ITestMethod.Invoke(object![]? arguments) -> Microsoft.VisualStudio.TestTools.UnitTesting.TestResult! +Microsoft.VisualStudio.TestTools.UnitTesting.ITestMethod.InvokeAsync(object![]? arguments) -> System.Threading.Tasks.Task! Microsoft.VisualStudio.TestTools.UnitTesting.ITestMethod.MethodInfo.get -> System.Reflection.MethodInfo! Microsoft.VisualStudio.TestTools.UnitTesting.ITestMethod.ParameterTypes.get -> System.Reflection.ParameterInfo![]! Microsoft.VisualStudio.TestTools.UnitTesting.ITestMethod.ReturnType.get -> System.Type! @@ -438,6 +453,8 @@ Microsoft.VisualStudio.TestTools.UnitTesting.TestDataRow.DisplayName.get -> s Microsoft.VisualStudio.TestTools.UnitTesting.TestDataRow.DisplayName.set -> void Microsoft.VisualStudio.TestTools.UnitTesting.TestDataRow.IgnoreMessage.get -> string? Microsoft.VisualStudio.TestTools.UnitTesting.TestDataRow.IgnoreMessage.set -> void +Microsoft.VisualStudio.TestTools.UnitTesting.TestDataRow.TestCategories.get -> System.Collections.Generic.IList? +Microsoft.VisualStudio.TestTools.UnitTesting.TestDataRow.TestCategories.set -> void Microsoft.VisualStudio.TestTools.UnitTesting.TestDataRow.TestDataRow(T value) -> void Microsoft.VisualStudio.TestTools.UnitTesting.TestDataRow.Value.get -> T Microsoft.VisualStudio.TestTools.UnitTesting.TestDataSourceDiscoveryAttribute @@ -512,6 +529,7 @@ Microsoft.VisualStudio.TestTools.UnitTesting.UnitTestAssertException Microsoft.VisualStudio.TestTools.UnitTesting.UnitTestAssertException.UnitTestAssertException() -> void Microsoft.VisualStudio.TestTools.UnitTesting.UnitTestAssertException.UnitTestAssertException(string! msg, System.Exception! ex) -> void Microsoft.VisualStudio.TestTools.UnitTesting.UnitTestAssertException.UnitTestAssertException(string! msg) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.UnitTestAssertException.UnitTestAssertException(System.Runtime.Serialization.SerializationInfo! info, System.Runtime.Serialization.StreamingContext context) -> void Microsoft.VisualStudio.TestTools.UnitTesting.UnitTestOutcome Microsoft.VisualStudio.TestTools.UnitTesting.UnitTestOutcome.Aborted = 6 -> Microsoft.VisualStudio.TestTools.UnitTesting.UnitTestOutcome Microsoft.VisualStudio.TestTools.UnitTesting.UnitTestOutcome.Error = 4 -> Microsoft.VisualStudio.TestTools.UnitTesting.UnitTestOutcome @@ -527,13 +545,19 @@ Microsoft.VisualStudio.TestTools.UnitTesting.UnitTestOutcome.Unknown = 7 -> Micr Microsoft.VisualStudio.TestTools.UnitTesting.WorkItemAttribute Microsoft.VisualStudio.TestTools.UnitTesting.WorkItemAttribute.Id.get -> int Microsoft.VisualStudio.TestTools.UnitTesting.WorkItemAttribute.WorkItemAttribute(int id) -> void +override Microsoft.VisualStudio.TestTools.UnitTesting.CIConditionAttribute.GroupName.get -> string! +override Microsoft.VisualStudio.TestTools.UnitTesting.CIConditionAttribute.IgnoreMessage.get -> string? +override Microsoft.VisualStudio.TestTools.UnitTesting.CIConditionAttribute.IgnoreMessage.set -> void +override Microsoft.VisualStudio.TestTools.UnitTesting.CIConditionAttribute.ShouldRun.get -> bool override Microsoft.VisualStudio.TestTools.UnitTesting.GenericParameterHelper.Equals(object? obj) -> bool override Microsoft.VisualStudio.TestTools.UnitTesting.GenericParameterHelper.GetHashCode() -> int override Microsoft.VisualStudio.TestTools.UnitTesting.IgnoreAttribute.GroupName.get -> string! override Microsoft.VisualStudio.TestTools.UnitTesting.IgnoreAttribute.IgnoreMessage.get -> string? +override Microsoft.VisualStudio.TestTools.UnitTesting.IgnoreAttribute.IgnoreMessage.set -> void override Microsoft.VisualStudio.TestTools.UnitTesting.IgnoreAttribute.ShouldRun.get -> bool override Microsoft.VisualStudio.TestTools.UnitTesting.OSConditionAttribute.GroupName.get -> string! override Microsoft.VisualStudio.TestTools.UnitTesting.OSConditionAttribute.IgnoreMessage.get -> string? +override Microsoft.VisualStudio.TestTools.UnitTesting.OSConditionAttribute.IgnoreMessage.set -> void override Microsoft.VisualStudio.TestTools.UnitTesting.OSConditionAttribute.ShouldRun.get -> bool override Microsoft.VisualStudio.TestTools.UnitTesting.TestCategoryAttribute.TestCategories.get -> System.Collections.Generic.IList! static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AreEqual(decimal expected, decimal actual, decimal delta, ref Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertNonGenericAreEqualInterpolatedStringHandler message) -> void @@ -618,8 +642,8 @@ static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.Contains(string! subs static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.Contains(string! substring, string! value, System.StringComparison comparisonType, string? message) -> void static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.Contains(string! substring, string! value, System.StringComparison comparisonType) -> void static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.Contains(string! substring, string! value) -> void -static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.Contains(System.Collections.Generic.IEnumerable! collection, System.Func! predicate, string? message) -> void static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.Contains(System.Func! predicate, System.Collections.Generic.IEnumerable! collection, string? message, params object?[]? parameters) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.Contains(System.Func! predicate, System.Collections.Generic.IEnumerable! collection, string? message) -> void static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.Contains(System.Func! predicate, System.Collections.Generic.IEnumerable! collection) -> void static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.Contains(T expected, System.Collections.Generic.IEnumerable! collection, string? message, params object?[]? parameters) -> void static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.Contains(T expected, System.Collections.Generic.IEnumerable! collection, string? message) -> void @@ -631,6 +655,7 @@ static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.ContainsSingle(Sys static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.ContainsSingle(System.Collections.Generic.IEnumerable! collection, string? message, params object?[]? parameters) -> T static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.ContainsSingle(System.Collections.Generic.IEnumerable! collection, string? message) -> T static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.ContainsSingle(System.Collections.Generic.IEnumerable! collection) -> T +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.ContainsSingle(System.Func! predicate, System.Collections.Generic.IEnumerable! collection, string! message = "") -> T static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.DoesNotContain(string! substring, string! value, string? message, params object?[]? parameters) -> void static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.DoesNotContain(string! substring, string! value, string? message) -> void static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.DoesNotContain(string! substring, string! value, System.StringComparison comparisonType, string? message, params object?[]? parameters) -> void @@ -646,6 +671,14 @@ static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.DoesNotContain(T e static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.DoesNotContain(T expected, System.Collections.Generic.IEnumerable! collection, System.Collections.Generic.IEqualityComparer! comparer, string? message) -> void static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.DoesNotContain(T expected, System.Collections.Generic.IEnumerable! collection, System.Collections.Generic.IEqualityComparer! comparer) -> void static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.DoesNotContain(T expected, System.Collections.Generic.IEnumerable! collection) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.DoesNotEndWith(string? substring, string? value, string! message = "") -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.DoesNotEndWith(string? substring, string? value, System.StringComparison comparisonType, string! message = "") -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.DoesNotMatchRegex(string? pattern, string? value, string! message = "") -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.DoesNotMatchRegex(System.Text.RegularExpressions.Regex? pattern, string? value, string! message = "") -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.DoesNotStartWith(string? substring, string? value, string! message = "") -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.DoesNotStartWith(string? substring, string? value, System.StringComparison comparisonType, string! message = "") -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.EndsWith(string? substring, string? value, string! message = "") -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.EndsWith(string? substring, string? value, System.StringComparison comparisonType, string! message = "") -> void static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.Equals(object? objA, object? objB) -> bool static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.Fail() -> void static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.Fail(string? message, params object?[]? parameters) -> void @@ -657,6 +690,7 @@ static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.HasCount(int expec static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.Inconclusive() -> void static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.Inconclusive(string? message, params object?[]? parameters) -> void static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.Inconclusive(string? message) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.Instance.get -> Microsoft.VisualStudio.TestTools.UnitTesting.Assert! static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.IsEmpty(System.Collections.Generic.IEnumerable! collection, ref Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertCountInterpolatedStringHandler message) -> void static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.IsEmpty(System.Collections.Generic.IEnumerable! collection, string? message, params object?[]? parameters) -> void static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.IsEmpty(System.Collections.Generic.IEnumerable! collection, string? message) -> void @@ -669,6 +703,9 @@ static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.IsFalse(bool? conditi static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.IsFalse(bool? condition, string? message, params object?[]? parameters) -> void static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.IsFalse(bool? condition, string? message) -> void static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.IsFalse(bool? condition) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.IsGreaterThan(T lowerBound, T value, string! message = "") -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.IsGreaterThanOrEqualTo(T lowerBound, T value, string! message = "") -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.IsInRange(T minValue, T maxValue, T value, string! message = "") -> void static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.IsInstanceOfType(object? value, System.Type? expectedType, ref Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsInstanceOfTypeInterpolatedStringHandler 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, System.Type? expectedType, string? message) -> void @@ -681,6 +718,9 @@ static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.IsInstanceOfType(o static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.IsInstanceOfType(object? value, 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) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.IsLessThan(T upperBound, T value, string! message = "") -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.IsLessThanOrEqualTo(T upperBound, T value, string! message = "") -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.IsNegative(T value, string! message = "") -> void static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.IsNotEmpty(System.Collections.Generic.IEnumerable! collection, ref Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsNotEmptyInterpolatedStringHandler message) -> void static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.IsNotEmpty(System.Collections.Generic.IEnumerable! collection, string? message, params object?[]? parameters) -> void static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.IsNotEmpty(System.Collections.Generic.IEnumerable! collection, string? message) -> void @@ -701,6 +741,7 @@ static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.IsNull(object? value, static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.IsNull(object? value, string? message, params object?[]? parameters) -> void static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.IsNull(object? value, string? message) -> void static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.IsNull(object? value) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.IsPositive(T value, string! message = "") -> void static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.IsTrue(bool condition, ref Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsTrueInterpolatedStringHandler message) -> void static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.IsTrue(bool condition, string? message, params object?[]? parameters) -> void static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.IsTrue(bool condition, string? message) -> void @@ -709,16 +750,27 @@ static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.IsTrue(bool? conditio static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.IsTrue(bool? condition, string? message, params object?[]? parameters) -> void static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.IsTrue(bool? condition, string? message) -> void static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.IsTrue(bool? condition) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.MatchesRegex(string? pattern, string? value, string! message = "") -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.MatchesRegex(System.Text.RegularExpressions.Regex? pattern, string? value, string! message = "") -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.ReferenceEquals(object? objA, object? objB) -> bool static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.ReplaceNullChars(string? input) -> string? +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.StartsWith(string? substring, string? value, string! message = "") -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.StartsWith(string? substring, string? value, System.StringComparison comparisonType, string! message = "") -> void static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.That.get -> Microsoft.VisualStudio.TestTools.UnitTesting.Assert! static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.Throws(System.Action! action, ref Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertNonStrictThrowsInterpolatedStringHandler message) -> TException! static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.Throws(System.Action! action, string! message = "", params object![]! messageArgs) -> TException! static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.Throws(System.Action! action, System.Func! messageBuilder) -> TException! +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.Throws(System.Func! action, ref Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertNonStrictThrowsInterpolatedStringHandler message) -> TException! +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.Throws(System.Func! action, string! message = "", params object![]! messageArgs) -> TException! +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.Throws(System.Func! action, System.Func! messageBuilder) -> TException! static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.ThrowsAsync(System.Func! action, string! message = "", params object![]! messageArgs) -> System.Threading.Tasks.Task! static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.ThrowsAsync(System.Func! action, System.Func! messageBuilder) -> System.Threading.Tasks.Task! static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.ThrowsExactly(System.Action! action, ref Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertThrowsExactlyInterpolatedStringHandler message) -> TException! static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.ThrowsExactly(System.Action! action, string! message = "", params object![]! messageArgs) -> TException! static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.ThrowsExactly(System.Action! action, System.Func! messageBuilder) -> TException! +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.ThrowsExactly(System.Func! action, ref Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertThrowsExactlyInterpolatedStringHandler message) -> TException! +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.ThrowsExactly(System.Func! action, string! message = "", params object![]! messageArgs) -> TException! +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.ThrowsExactly(System.Func! action, System.Func! messageBuilder) -> TException! static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.ThrowsExactlyAsync(System.Func! action, string! message = "", params object![]! messageArgs) -> System.Threading.Tasks.Task! static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.ThrowsExactlyAsync(System.Func! action, System.Func! messageBuilder) -> System.Threading.Tasks.Task! static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.ThrowsException(System.Action! action, string! message, params object?[]? parameters) -> T! @@ -769,12 +821,15 @@ static Microsoft.VisualStudio.TestTools.UnitTesting.CollectionAssert.Contains(Sy static Microsoft.VisualStudio.TestTools.UnitTesting.CollectionAssert.DoesNotContain(System.Collections.ICollection? collection, object? element, string? message, params object?[]? parameters) -> void static Microsoft.VisualStudio.TestTools.UnitTesting.CollectionAssert.DoesNotContain(System.Collections.ICollection? collection, object? element, string? message) -> void static Microsoft.VisualStudio.TestTools.UnitTesting.CollectionAssert.DoesNotContain(System.Collections.ICollection? collection, object? element) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.CollectionAssert.Equals(object? objA, object? objB) -> bool +static Microsoft.VisualStudio.TestTools.UnitTesting.CollectionAssert.Instance.get -> Microsoft.VisualStudio.TestTools.UnitTesting.CollectionAssert! static Microsoft.VisualStudio.TestTools.UnitTesting.CollectionAssert.IsNotSubsetOf(System.Collections.ICollection? subset, System.Collections.ICollection? superset, string? message, params object?[]? parameters) -> void static Microsoft.VisualStudio.TestTools.UnitTesting.CollectionAssert.IsNotSubsetOf(System.Collections.ICollection? subset, System.Collections.ICollection? superset, string? message) -> void static Microsoft.VisualStudio.TestTools.UnitTesting.CollectionAssert.IsNotSubsetOf(System.Collections.ICollection? subset, System.Collections.ICollection? superset) -> void static Microsoft.VisualStudio.TestTools.UnitTesting.CollectionAssert.IsSubsetOf(System.Collections.ICollection? subset, System.Collections.ICollection? superset, string? message, params object?[]? parameters) -> void static Microsoft.VisualStudio.TestTools.UnitTesting.CollectionAssert.IsSubsetOf(System.Collections.ICollection? subset, System.Collections.ICollection? superset, string? message) -> void static Microsoft.VisualStudio.TestTools.UnitTesting.CollectionAssert.IsSubsetOf(System.Collections.ICollection? subset, System.Collections.ICollection? superset) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.CollectionAssert.ReferenceEquals(object? objA, object? objB) -> bool static Microsoft.VisualStudio.TestTools.UnitTesting.CollectionAssert.That.get -> Microsoft.VisualStudio.TestTools.UnitTesting.CollectionAssert! static Microsoft.VisualStudio.TestTools.UnitTesting.DataRowAttribute.TestIdGenerationStrategy.get -> Microsoft.VisualStudio.TestTools.UnitTesting.TestIdGenerationStrategy static Microsoft.VisualStudio.TestTools.UnitTesting.Logging.Logger.LogMessage(string! format, params object?[]! args) -> void @@ -794,9 +849,12 @@ static Microsoft.VisualStudio.TestTools.UnitTesting.StringAssert.EndsWith(string static Microsoft.VisualStudio.TestTools.UnitTesting.StringAssert.EndsWith(string? value, string? substring, string? message) -> void static Microsoft.VisualStudio.TestTools.UnitTesting.StringAssert.EndsWith(string? value, string? substring, System.StringComparison comparisonType) -> void static Microsoft.VisualStudio.TestTools.UnitTesting.StringAssert.EndsWith(string? value, string? substring) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.StringAssert.Equals(object? objA, object? objB) -> bool +static Microsoft.VisualStudio.TestTools.UnitTesting.StringAssert.Instance.get -> Microsoft.VisualStudio.TestTools.UnitTesting.StringAssert! static Microsoft.VisualStudio.TestTools.UnitTesting.StringAssert.Matches(string? value, System.Text.RegularExpressions.Regex? pattern, string? message, params object?[]? parameters) -> void static Microsoft.VisualStudio.TestTools.UnitTesting.StringAssert.Matches(string? value, System.Text.RegularExpressions.Regex? pattern, string? message) -> void static Microsoft.VisualStudio.TestTools.UnitTesting.StringAssert.Matches(string? value, System.Text.RegularExpressions.Regex? pattern) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.StringAssert.ReferenceEquals(object? objA, object? objB) -> bool static Microsoft.VisualStudio.TestTools.UnitTesting.StringAssert.StartsWith(string? value, string? substring, string? message, params object?[]? parameters) -> void static Microsoft.VisualStudio.TestTools.UnitTesting.StringAssert.StartsWith(string? value, string? substring, string? message, System.StringComparison comparisonType, params object?[]? parameters) -> void static Microsoft.VisualStudio.TestTools.UnitTesting.StringAssert.StartsWith(string? value, string? substring, string? message, System.StringComparison comparisonType) -> void @@ -807,6 +865,8 @@ static Microsoft.VisualStudio.TestTools.UnitTesting.StringAssert.That.get -> Mic static readonly Microsoft.VisualStudio.TestTools.UnitTesting.ClassCleanupExecutionAttribute.DefaultClassCleanupLifecycle -> Microsoft.VisualStudio.TestTools.UnitTesting.ClassCleanupBehavior static readonly Microsoft.VisualStudio.TestTools.UnitTesting.DataSourceAttribute.DefaultDataAccessMethod -> Microsoft.VisualStudio.TestTools.UnitTesting.DataAccessMethod static readonly Microsoft.VisualStudio.TestTools.UnitTesting.DataSourceAttribute.DefaultProviderName -> string! +virtual Microsoft.VisualStudio.TestTools.UnitTesting.ConditionBaseAttribute.IgnoreMessage.get -> string? +virtual Microsoft.VisualStudio.TestTools.UnitTesting.ConditionBaseAttribute.IgnoreMessage.set -> void virtual Microsoft.VisualStudio.TestTools.UnitTesting.DataRowAttribute.GetDisplayName(System.Reflection.MethodInfo! methodInfo, object?[]? data) -> string? virtual Microsoft.VisualStudio.TestTools.UnitTesting.ExpectedExceptionBaseAttribute.NoExceptionMessage.get -> string! virtual Microsoft.VisualStudio.TestTools.UnitTesting.Logging.Logger.LogMessageHandler.Invoke(string! message) -> void diff --git a/src/TestFramework/TestFramework/PublicAPI/PublicAPI.Unshipped.txt b/src/TestFramework/TestFramework/PublicAPI/PublicAPI.Unshipped.txt index 284951033c..cf84db7811 100644 --- a/src/TestFramework/TestFramework/PublicAPI/PublicAPI.Unshipped.txt +++ b/src/TestFramework/TestFramework/PublicAPI/PublicAPI.Unshipped.txt @@ -1,11 +1,23 @@ #nullable enable -Microsoft.VisualStudio.TestTools.UnitTesting.AssertFailedException.AssertFailedException(System.Runtime.Serialization.SerializationInfo! info, System.Runtime.Serialization.StreamingContext context) -> void -Microsoft.VisualStudio.TestTools.UnitTesting.AssertInconclusiveException.AssertInconclusiveException(System.Runtime.Serialization.SerializationInfo! info, System.Runtime.Serialization.StreamingContext context) -> void -Microsoft.VisualStudio.TestTools.UnitTesting.InternalTestFailureException.InternalTestFailureException(System.Runtime.Serialization.SerializationInfo! info, System.Runtime.Serialization.StreamingContext context) -> void -Microsoft.VisualStudio.TestTools.UnitTesting.ITestMethod.InvokeAsync(object![]? arguments) -> System.Threading.Tasks.Task! -Microsoft.VisualStudio.TestTools.UnitTesting.UnitTestAssertException.UnitTestAssertException(System.Runtime.Serialization.SerializationInfo! info, System.Runtime.Serialization.StreamingContext context) -> void -static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.Throws(System.Func! action, string! message = "", params object![]! messageArgs) -> TException! -static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.Throws(System.Func! action, System.Func! messageBuilder) -> TException! -static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.ThrowsExactly(System.Func! action, string! message = "", params object![]! messageArgs) -> TException! -static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.ThrowsExactly(System.Func! action, System.Func! messageBuilder) -> TException! -[MSTESTEXP]Microsoft.VisualStudio.TestTools.UnitTesting.RetryContext.FirstRunResults.get -> Microsoft.VisualStudio.TestTools.UnitTesting.TestResult![]! +Microsoft.VisualStudio.TestTools.UnitTesting.DynamicDataSourceType.Field = 3 -> Microsoft.VisualStudio.TestTools.UnitTesting.DynamicDataSourceType +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.Contains(object! expected, System.Collections.IEnumerable! collection, System.Collections.IEqualityComparer! comparer) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.Contains(object! expected, System.Collections.IEnumerable! collection, System.Collections.IEqualityComparer! comparer, string? message) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.Contains(object? expected, System.Collections.IEnumerable! collection) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.Contains(object? expected, System.Collections.IEnumerable! collection, string? message) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.HasCount(int expected, System.Collections.IEnumerable! collection) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.HasCount(int expected, System.Collections.IEnumerable! collection, string? message) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.IsEmpty(System.Collections.IEnumerable! collection) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.IsEmpty(System.Collections.IEnumerable! collection, string? message) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.IsNotEmpty(System.Collections.IEnumerable! collection) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.IsNotEmpty(System.Collections.IEnumerable! collection, string? message) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.DoesNotContain(object? expected, System.Collections.IEnumerable! collection) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.DoesNotContain(object? expected, System.Collections.IEnumerable! collection, string? message) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.DoesNotContain(object? expected, System.Collections.IEnumerable! collection, System.Collections.IEqualityComparer! comparer) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.DoesNotContain(object? expected, System.Collections.IEnumerable! collection, System.Collections.IEqualityComparer! comparer, string? message) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.DoesNotContain(System.Func! predicate, System.Collections.IEnumerable! collection) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.DoesNotContain(System.Func! predicate, System.Collections.IEnumerable! collection, string? message) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.Contains(System.Func! predicate, System.Collections.IEnumerable! collection) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.Contains(System.Func! predicate, System.Collections.IEnumerable! collection, string? message) -> void +*REMOVED*static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.Instance.get -> Microsoft.VisualStudio.TestTools.UnitTesting.Assert! +*REMOVED*static Microsoft.VisualStudio.TestTools.UnitTesting.CollectionAssert.Instance.get -> Microsoft.VisualStudio.TestTools.UnitTesting.CollectionAssert! +*REMOVED*static Microsoft.VisualStudio.TestTools.UnitTesting.StringAssert.Instance.get -> Microsoft.VisualStudio.TestTools.UnitTesting.StringAssert! diff --git a/src/TestFramework/TestFramework/Resources/FrameworkMessages.Designer.cs b/src/TestFramework/TestFramework/Resources/FrameworkMessages.Designer.cs deleted file mode 100644 index 51c9d1dd52..0000000000 --- a/src/TestFramework/TestFramework/Resources/FrameworkMessages.Designer.cs +++ /dev/null @@ -1,586 +0,0 @@ -//------------------------------------------------------------------------------ -// -// 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.VisualStudio.TestTools.UnitTesting { - 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 FrameworkMessages { - - private static global::System.Resources.ResourceManager resourceMan; - - private static global::System.Globalization.CultureInfo resourceCulture; - - [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] - internal FrameworkMessages() { - } - - /// - /// 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.VisualStudio.TestTools.UnitTesting.Resources.FrameworkMessages", typeof(FrameworkMessages).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 Access string has invalid syntax.. - /// - internal static string AccessStringInvalidSyntax { - get { - return ResourceManager.GetString("AccessStringInvalidSyntax", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to The expected collection contains {1} occurrence(s) of <{2}>. The actual collection contains {3} occurrence(s). {0}. - /// - internal static string ActualHasMismatchedElements { - get { - return ResourceManager.GetString("ActualHasMismatchedElements", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Duplicate item found:<{1}>. {0}. - /// - internal static string AllItemsAreUniqueFailMsg { - get { - return ResourceManager.GetString("AllItemsAreUniqueFailMsg", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Expected:<{1}>. Case is different for actual value:<{2}>. {0}. - /// - internal static string AreEqualCaseFailMsg { - get { - return ResourceManager.GetString("AreEqualCaseFailMsg", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Expected a difference no greater than <{3}> between expected value <{1}> and actual value <{2}>. {0}. - /// - internal static string AreEqualDeltaFailMsg { - get { - return ResourceManager.GetString("AreEqualDeltaFailMsg", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Expected:<{1} ({2})>. Actual:<{3} ({4})>. {0}. - /// - internal static string AreEqualDifferentTypesFailMsg { - get { - return ResourceManager.GetString("AreEqualDifferentTypesFailMsg", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Expected:<{1}>. Actual:<{2}>. {0}. - /// - internal static string AreEqualFailMsg { - get { - return ResourceManager.GetString("AreEqualFailMsg", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Expected a difference greater than <{3}> between expected value <{1}> and actual value <{2}>. {0}. - /// - internal static string AreNotEqualDeltaFailMsg { - get { - return ResourceManager.GetString("AreNotEqualDeltaFailMsg", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Expected any value except:<{1}>. Actual:<{2}>. {0}. - /// - internal static string AreNotEqualFailMsg { - get { - return ResourceManager.GetString("AreNotEqualFailMsg", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Do not pass value types to AreSame(). Values converted to Object will never be the same. Consider using AreEqual(). {0}. - /// - internal static string AreSameGivenValues { - get { - return ResourceManager.GetString("AreSameGivenValues", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Type '{0}' is not assignable to '{1}'.. - /// - internal static string ArgumentXMustDeriveFromClassY { - get { - return ResourceManager.GetString("ArgumentXMustDeriveFromClassY", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to {0} failed. {1}. - /// - internal static string AssertionFailed { - get { - return ResourceManager.GetString("AssertionFailed", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to async TestMethod with UITestMethodAttribute are not supported. Either remove async or use TestMethodAttribute.. - /// - internal static string AsyncUITestMethodNotSupported { - get { - return ResourceManager.GetString("AsyncUITestMethodNotSupported", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to UITestMethodAttribute.DispatcherQueue should not be null. To use UITestMethodAttribute within a WinUI Desktop App, remember to set the static UITestMethodAttribute.DispatcherQueue during the test initialization.. - /// - internal static string AsyncUITestMethodWithNoDispatcherQueue { - get { - return ResourceManager.GetString("AsyncUITestMethodWithNoDispatcherQueue", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Both collections are empty. {0}. - /// - internal static string BothCollectionsEmpty { - get { - return ResourceManager.GetString("BothCollectionsEmpty", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Both collection contain same elements.. - /// - internal static string BothCollectionsSameElements { - get { - return ResourceManager.GetString("BothCollectionsSameElements", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Both collection references point to the same collection object. {0}. - /// - internal static string BothCollectionsSameReference { - get { - return ResourceManager.GetString("BothCollectionsSameReference", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Both collections contain the same elements. {0}. - /// - internal static string BothSameElements { - get { - return ResourceManager.GetString("BothSameElements", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to {0}. {1}. - /// - internal static string CollectionEqualReason { - get { - return ResourceManager.GetString("CollectionEqualReason", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to (null). - /// - internal static string Common_NullInMessages { - get { - return ResourceManager.GetString("Common_NullInMessages", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to (object). - /// - internal static string Common_ObjectString { - get { - return ResourceManager.GetString("Common_ObjectString", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to String '{0}' does not contain string '{1}'. {2}.. - /// - internal static string ContainsFail { - get { - return ResourceManager.GetString("ContainsFail", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to {0} ({1}). - /// - internal static string DataDrivenResultDisplayName { - get { - return ResourceManager.GetString("DataDrivenResultDisplayName", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to String '{0}' does contain string '{1}'. {2}.. - /// - internal static string DoesNotContainFail { - get { - return ResourceManager.GetString("DoesNotContainFail", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Assert.Equals should not be used for Assertions. Please use Assert.AreEqual & overloads instead.. - /// - internal static string DoNotUseAssertEquals { - get { - return ResourceManager.GetString("DoNotUseAssertEquals", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Method {0} must match the expected signature: public static {1} {0}({2}).. - /// - internal static string DynamicDataDisplayName { - get { - return ResourceManager.GetString("DynamicDataDisplayName", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Property or method {0} on {1} returns empty IEnumerable<object[]>.. - /// - internal static string DynamicDataIEnumerableEmpty { - get { - return ResourceManager.GetString("DynamicDataIEnumerableEmpty", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Property or method {0} on {1} return type is not assignable to 'IEnumerable'.. - /// - internal static string DynamicDataIEnumerableNull { - get { - return ResourceManager.GetString("DynamicDataIEnumerableNull", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Dynamic data method '{0}' should be static, parameterless and non-generic.. - /// - internal static string DynamicDataInvalidMethodLayout { - get { - return ResourceManager.GetString("DynamicDataInvalidMethodLayout", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Dynamic data property '{0}' should be static and have a getter.. - /// - internal static string DynamicDataInvalidPropertyLayout { - get { - return ResourceManager.GetString("DynamicDataInvalidPropertyLayout", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to The dynamic data source '{0}' in type '{1}' should exist and be a property or a method.. - /// - internal static string DynamicDataSourceShouldExistAndBeValid { - get { - return ResourceManager.GetString("DynamicDataSourceShouldExistAndBeValid", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Value returned by property or method {0} shouldn't be null.. - /// - internal static string DynamicDataValueNull { - get { - return ResourceManager.GetString("DynamicDataValueNull", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to The number of elements in the collections do not match. Expected:<{1}>. Actual:<{2}>.{0}. - /// - internal static string ElementNumbersDontMatch { - get { - return ResourceManager.GetString("ElementNumbersDontMatch", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Element at index {0} do not match. - ///Expected: {1} - ///Actual: {2}. - /// - internal static string ElementsAtIndexDontMatch { - get { - return ResourceManager.GetString("ElementsAtIndexDontMatch", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Element at index {1} is not of expected type. Expected type:<{2}>. Actual type:<{3}>.{0}. - /// - internal static string ElementTypesAtIndexDontMatch { - get { - return ResourceManager.GetString("ElementTypesAtIndexDontMatch", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to String '{0}' does not end with string '{1}'. {2}.. - /// - internal static string EndsWithFail { - get { - return ResourceManager.GetString("EndsWithFail", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Expected collection of size {1}. Actual: {2}. {0}. - /// - internal static string HasCountFailMsg { - get { - return ResourceManager.GetString("HasCountFailMsg", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Invalid GitHub ticket URL. - /// - internal static string InvalidGitHubUrl { - get { - return ResourceManager.GetString("InvalidGitHubUrl", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to The property {0} has type {1}; expected type {2}.. - /// - internal static string InvalidPropertyType { - get { - return ResourceManager.GetString("InvalidPropertyType", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to {0} Expected type:<{1}>. Actual type:<{2}>.. - /// - internal static string IsInstanceOfFailMsg { - get { - return ResourceManager.GetString("IsInstanceOfFailMsg", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to String '{0}' does not match pattern '{1}'. {2}.. - /// - internal static string IsMatchFail { - get { - return ResourceManager.GetString("IsMatchFail", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Expected collection to contain any item but it is empty. {0}. - /// - internal static string IsNotEmptyFailMsg { - get { - return ResourceManager.GetString("IsNotEmptyFailMsg", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Wrong Type:<{1}>. Actual type:<{2}>. {0}. - /// - internal static string IsNotInstanceOfFailMsg { - get { - return ResourceManager.GetString("IsNotInstanceOfFailMsg", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to String '{0}' matches pattern '{1}'. {2}.. - /// - internal static string IsNotMatchFail { - get { - return ResourceManager.GetString("IsNotMatchFail", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Expected exception type:<{1}> but no exception was thrown. {0}. - /// - internal static string NoExceptionThrown { - get { - return ResourceManager.GetString("NoExceptionThrown", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to The parameter '{0}' is invalid. The value cannot be null. {1}.. - /// - internal static string NullParameterToAssert { - get { - return ResourceManager.GetString("NullParameterToAssert", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Different number of elements.. - /// - internal static string NumberOfElementsDiff { - get { - return ResourceManager.GetString("NumberOfElementsDiff", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to - /// The constructor with the specified signature could not be found. You might need to regenerate your private accessor, - /// or the member may be private and defined on a base class. If the latter is true, you need to pass the type - /// that defines the member into PrivateObject's constructor. - /// . - /// - internal static string PrivateAccessorConstructorNotFound { - get { - return ResourceManager.GetString("PrivateAccessorConstructorNotFound", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to - /// The member specified ({0}) could not be found. You might need to regenerate your private accessor, - /// or the member may be private and defined on a base class. If the latter is true, you need to pass the type - /// that defines the member into PrivateObject's constructor. - /// . - /// - internal static string PrivateAccessorMemberNotFound { - get { - return ResourceManager.GetString("PrivateAccessorMemberNotFound", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to String '{0}' does not start with string '{1}'. {2}.. - /// - internal static string StartsWithFail { - get { - return ResourceManager.GetString("StartsWithFail", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to The expected exception type must be System.Exception or a type derived from System.Exception.. - /// - internal static string UTF_ExpectedExceptionTypeMustDeriveFromException { - get { - return ResourceManager.GetString("UTF_ExpectedExceptionTypeMustDeriveFromException", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to (Failed to get the message for an exception of type {0} due to an exception.). - /// - internal static string UTF_FailedToGetExceptionMessage { - get { - return ResourceManager.GetString("UTF_FailedToGetExceptionMessage", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Test method did not throw expected exception {0}. {1}. - /// - internal static string UTF_TestMethodNoException { - get { - return ResourceManager.GetString("UTF_TestMethodNoException", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Test method did not throw an exception. An exception was expected by attribute {0} defined on the test method.. - /// - internal static string UTF_TestMethodNoExceptionDefault { - get { - return ResourceManager.GetString("UTF_TestMethodNoExceptionDefault", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Test method threw exception {0}, but exception {1} was expected. Exception message: {2}. - /// - internal static string UTF_TestMethodWrongException { - get { - return ResourceManager.GetString("UTF_TestMethodWrongException", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Test method threw exception {0}, but exception {1} or a type derived from it was expected. Exception message: {2}. - /// - internal static string UTF_TestMethodWrongExceptionDerivedAllowed { - get { - return ResourceManager.GetString("UTF_TestMethodWrongExceptionDerivedAllowed", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Expected exception type:<{1}>. Actual exception type:<{2}>. {0}. - /// - internal static string WrongExceptionThrown { - get { - return ResourceManager.GetString("WrongExceptionThrown", resourceCulture); - } - } - } -} diff --git a/src/TestFramework/TestFramework/Resources/FrameworkMessages.resx b/src/TestFramework/TestFramework/Resources/FrameworkMessages.resx index ceca8be756..2de03a6968 100644 --- a/src/TestFramework/TestFramework/Resources/FrameworkMessages.resx +++ b/src/TestFramework/TestFramework/Resources/FrameworkMessages.resx @@ -138,6 +138,24 @@ Expected:<{1} ({2})>. Actual:<{3} ({4})>. {0} + + {0}{1} +{2} +{3} +{4} + + + Expected: + + + But was: + + + String lengths are both {0} but differ at index {1}. + + + Expected string length {0} but was {1}. + Expected any value except:<{1}>. Actual:<{2}>. {0} @@ -165,6 +183,9 @@ String '{0}' does not contain string '{1}'. {2}. + + Value '{0}' is not within the expected range [{1}, {2}]. {3} + The number of elements in the collections do not match. Expected:<{1}>. Actual:<{2}>.{0} @@ -177,7 +198,7 @@ Actual: {2} Element at index {1} is not of expected type. Expected type:<{2}>. Actual type:<{3}>.{0} - String '{0}' does not end with string '{1}'. {2}. + String '{0}' does not end with string '{1}'. {2} {0} failed. {1} @@ -186,16 +207,13 @@ Actual: {2} {0} Expected type:<{1}>. Actual type:<{2}>. - String '{0}' does not match pattern '{1}'. {2}. + String '{0}' does not match pattern '{1}'. {2} Wrong Type:<{1}>. Actual type:<{2}>. {0} - String '{0}' matches pattern '{1}'. {2}. - - - Assert.Equals should not be used for Assertions. Please use Assert.AreEqual & overloads instead. + String '{0}' matches pattern '{1}'. {2} @@ -218,7 +236,7 @@ Actual: {2} Different number of elements. - String '{0}' does not start with string '{1}'. {2}. + String '{0}' does not start with string '{1}'. {2} The property {0} has type {1}; expected type {2}. @@ -256,12 +274,6 @@ Actual: {2} {0} ({1}) - - async TestMethod with UITestMethodAttribute are not supported. Either remove async or use TestMethodAttribute. - - - UITestMethodAttribute.DispatcherQueue should not be null. To use UITestMethodAttribute within a WinUI Desktop App, remember to set the static UITestMethodAttribute.DispatcherQueue during the test initialization. - Property or method {0} on {1} return type is not assignable to 'IEnumerable'. @@ -274,25 +286,39 @@ Actual: {2} Property or method {0} on {1} returns empty IEnumerable<object[]>. - - Type '{0}' is not assignable to '{1}'. - - - {0} argument name like "applicationType" - - {1} fully qualified class name like "Microsoft.UI.Xaml.Application" - - - Dynamic data method '{0}' should be static, parameterless and non-generic. + Dynamic data method '{0}' should be static, non-generic, and cannot have 'params' parameter. Dynamic data property '{0}' should be static and have a getter. + + Dynamic data field '{0}' should be static. + - The dynamic data source '{0}' in type '{1}' should exist and be a property or a method. + The dynamic data source '{0}' in type '{1}' should exist and be a property, a method, or a field. Expected collection of size {1}. Actual: {2}. {0} + + Expected exactly one item to match the predicate but found {1} item(s). {0} + + + Expected collection to contain exactly one element but found {1} element(s). {0} + + + Expected collection to contain the specified item. {0} + + + Expected at least one item to match the predicate. {0} + + + Expected collection to not contain the specified item. {0} + + + Expected no items to match the predicate. {0} + Expected collection to contain any item but it is empty. {0} @@ -302,4 +328,49 @@ Actual: {2} String '{0}' does contain string '{1}'. {2}. + + Actual value <{2}> is not greater than expected value <{1}>. {0} + + + Actual value <{2}> is not greater than or equal to expected value <{1}>. {0} + + + Actual value <{2}> is not less than expected value <{1}>. {0} + + + Actual value <{2}> is not less than or equal to expected value <{1}>. {0} + + + Expected value <{1}> to be positive. {0} + + + Expected value <{1}> to be negative. {0} + + + String '{0}' ends with string '{1}'. {2} + + + String '{0}' starts with string '{1}'. {2} + + + Assert.Equals should not be used for Assertions. Please use Assert.AreEqual & overloads instead. + + + Assert.ReferenceEquals should not be used for Assertions. Please use Assert.AreSame & overloads instead. + + + StringAssert.Equals should not be used for Assertions. Please use StringAssert methods or Assert.AreEqual & overloads instead. + + + StringAssert.ReferenceEquals should not be used for Assertions. Please use StringAssert methods or Assert.AreSame & overloads instead. + + + CollectionAssert.Equals should not be used for Assertions. Please use CollectionAssert.AreEqual & overloads instead. + + + CollectionAssert.ReferenceEquals should not be used for Assertions. Please use CollectionAssert methods or Assert.AreSame & overloads instead. + + + Element(s) <{0}> is/are not present in the collection. + \ No newline at end of file diff --git a/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.cs.xlf b/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.cs.xlf index ddfcc9862b..7f5e380557 100644 --- a/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.cs.xlf +++ b/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.cs.xlf @@ -37,6 +37,37 @@ Očekáváno:<{1} ({2})>. Aktuálně:<{3} ({4})>. {0} + + {0}{1} +{2} +{3} +{4} + {0}{1} +{2} +{3} +{4} + + + + Expected: + Očekáváno: + + + + But was: + Ale bylo: + + + + String lengths are both {0} but differ at index {1}. + Délky obou řetězců jsou {0}, ale liší se na indexu {1}. + + + + Expected string length {0} but was {1}. + Očekávaná délka řetězce je {0}, ale byla {1}. + + Expected any value except:<{1}>. Actual:<{2}>. {0} Nebyla očekávána žádná hodnota kromě:<{1}>. Aktuálně:<{2}>. {0} @@ -82,14 +113,59 @@ Řetězec '{0}' neobsahuje řetězec '{1}'. {2}. + + Expected collection to contain the specified item. {0} + Očekávala se kolekce, která bude obsahovat zadanou položku. {0} + + + + Expected at least one item to match the predicate. {0} + Očekávala se alespoň jedna položka odpovídající predikátu. {0} + + + + Expected collection to contain exactly one element but found {1} element(s). {0} + Očekávalo se, že kolekce bude obsahovat přesně jeden prvek, ale našlo se tolik elementů: {1}. {0} + + + + Expected exactly one item to match the predicate but found {1} item(s). {0} + Očekávala se přesně jedna položka odpovídající predikátu, ale našlo se tolik položek: {1}. {0} + + String '{0}' does contain string '{1}'. {2}. Řetězec {0} obsahuje řetězec {1}. {2}. + + Expected collection to not contain the specified item. {0} + Očekávalo se, že kolekce nebude obsahovat zadanou položku. {0} + + + + Expected no items to match the predicate. {0} + Očekávaly se žádné položky, které by odpovídaly predikátu. {0} + + + + String '{0}' ends with string '{1}'. {2} + Řetězec „{0}“ končí řetězcem „{1}“. {2} + + + + String '{0}' starts with string '{1}'. {2} + Řetězec „{0}“ začíná řetězcem „{1}“. {2} + + + + Dynamic data field '{0}' should be static. + Dynamické datové pole {0} by mělo být statické. + + - Dynamic data method '{0}' should be static, parameterless and non-generic. - Metoda dynamických dat {0} by měla být statická, bez parametrů a negenerická. + Dynamic data method '{0}' should be static, non-generic, and cannot have 'params' parameter. + Dynamická datová metoda {0} by měla být statická a neobecná a nemůže mít parametr params. @@ -98,8 +174,8 @@ - The dynamic data source '{0}' in type '{1}' should exist and be a property or a method. - Dynamický zdroj dat '{0}' v typu '{1}' by měl existovat a být vlastností nebo metodou. + The dynamic data source '{0}' in type '{1}' should exist and be a property, a method, or a field. + Dynamický zdroj dat {0} v typu {1} by měl existovat a měla by to být vlastnost, metoda nebo pole. @@ -122,9 +198,9 @@ Skutečnost: {2} - String '{0}' does not end with string '{1}'. {2}. - Řetězec '{0}' nekončí řetězcem '{1}'. {2}. - + String '{0}' does not end with string '{1}'. {2} + Řetězec „{0}“ nekončí řetězcem „{1}“. {2} + {0} failed. {1} @@ -141,15 +217,20 @@ Skutečnost: {2} Neplatná adresa URL lístku GitHubu + + Value '{0}' is not within the expected range [{1}, {2}]. {3} + Hodnota {0} není v očekávaném rozsahu [{1}, {2}]. {3} + + {0} Expected type:<{1}>. Actual type:<{2}>. {0} Očekávaný typ:<{1}>. Aktuální typ:<{2}>. - String '{0}' does not match pattern '{1}'. {2}. - Řetězec '{0}' neodpovídá vzoru '{1}'. {2}. - + String '{0}' does not match pattern '{1}'. {2} + Řetězec „{0}“ neodpovídá vzoru „{1}“. {2} + Expected collection to contain any item but it is empty. {0} @@ -162,14 +243,9 @@ Skutečnost: {2} - String '{0}' matches pattern '{1}'. {2}. - Řetězec '{0}' odpovídá vzoru '{1}'. {2}. - - - - Assert.Equals should not be used for Assertions. Please use Assert.AreEqual & overloads instead. - Assert.Equals by neměla být pro kontrolní výrazy používána. Použijte prosím místo toho Assert.AreEqual a její přetížení. - + String '{0}' matches pattern '{1}'. {2} + Řetězec „{0}“ odpovídá vzoru „{1}“. {2} + @@ -182,7 +258,7 @@ Skutečnost: {2} nebo může být člen soukromý a definovaný v základní třídě. Pokud je tvrzení pravdivé, bude zapotřebí předat typ , který definuje člena do konstruktoru PrivateObject. - + @@ -195,7 +271,7 @@ Skutečnost: {2} nebo může být člen soukromý a definovaný v základní třídě. Pokud je tvrzení pravdivé, bude zapotřebí předat typ , který definuje člena do konstruktoru PrivateObject. - + The parameter '{0}' is invalid. The value cannot be null. {1}. @@ -207,10 +283,15 @@ Skutečnost: {2} Rozdílný počet elementů. + + Element(s) <{0}> is/are not present in the collection. + Element(s) <{0}> is/are not present in the collection. + + - String '{0}' does not start with string '{1}'. {2}. - Řetězec '{0}' nezačíná řetězcem '{1}'. {2}. - + String '{0}' does not start with string '{1}'. {2} + Řetězec „{0}“ nezačíná řetězcem „{1}“. {2} + The property {0} has type {1}; expected type {2}. @@ -272,11 +353,6 @@ Skutečnost: {2} {0} ({1}) - - async TestMethod with UITestMethodAttribute are not supported. Either remove async or use TestMethodAttribute. - async TestMethod s atributem UITestMethodAttribute se nepodporují. Buď odeberte async, nebo použijte TestMethodAttribute. - - Property or method {0} on {1} return type is not assignable to 'IEnumerable'. Vlastnost nebo metoda {0} na návratovém typu {1} se nedá přiřadit k „IEnumerable“. @@ -297,18 +373,65 @@ Skutečnost: {2} Vlastnost nebo metoda {0} ve třídě {1} vrací prázdné IEnumerable<object[]>. - - UITestMethodAttribute.DispatcherQueue should not be null. To use UITestMethodAttribute within a WinUI Desktop App, remember to set the static UITestMethodAttribute.DispatcherQueue during the test initialization. - UITestMethodAttribute.DispatcherQueue nesmí mít hodnotu null. Pokud chcete použít UITestMethodAttribute v desktopové aplikaci WinUI, nezapomeňte během inicializace testu nastavit statický UITestMethodAttribute.DispatcherQueue. + + Actual value <{2}> is not greater than expected value <{1}>. {0} + Skutečná hodnota <{2}> není větší než očekávaná hodnota <{1}>. {0} + + + + Actual value <{2}> is not greater than or equal to expected value <{1}>. {0} + Skutečná hodnota <{2}> není větší nebo rovna očekávané hodnotě <{1}>. {0} + + + + Actual value <{2}> is not less than expected value <{1}>. {0} + Skutečná hodnota <{2}> není menší než očekávaná hodnota <{1}>. {0} + + + + Actual value <{2}> is not less than or equal to expected value <{1}>. {0} + Skutečná hodnota <{2}> není menší nebo rovna očekávané hodnotě <{1}>. {0} - - Type '{0}' is not assignable to '{1}'. - Typ „{0}“ nelze přiřadit k „{1}“. - - - {0} argument name like "applicationType" - - {1} fully qualified class name like "Microsoft.UI.Xaml.Application" - + + Expected value <{1}> to be positive. {0} + Očekávaná hodnota <{1}> má být kladná. {0} + + + + Expected value <{1}> to be negative. {0} + Očekávaná hodnota <{1}> má být záporná. {0} + + + + Assert.Equals should not be used for Assertions. Please use Assert.AreEqual & overloads instead. + Assert.Equals by neměla být pro kontrolní výrazy používána. Použijte prosím místo toho Assert.AreEqual a její přetížení. + + + + Assert.ReferenceEquals should not be used for Assertions. Please use Assert.AreSame & overloads instead. + Assert.ReferenceEquals se nemá používat pro kontrolní výrazy. Použijte prosím místo toho Assert.AreEqual & overloads. + + + + StringAssert.Equals should not be used for Assertions. Please use StringAssert methods or Assert.AreEqual & overloads instead. + StringAssert.Equals se nemá používat pro kontrolní výrazy. Místo toho použijte metody StringAssert nebo Assert.AreEqual & overloads. + + + + StringAssert.ReferenceEquals should not be used for Assertions. Please use StringAssert methods or Assert.AreSame & overloads instead. + StringAssert.ReferenceEquals se nemá používat pro kontrolní výrazy. Místo toho použijte metody StringAssert nebo Assert.AreSame & overloads. + + + + CollectionAssert.Equals should not be used for Assertions. Please use CollectionAssert.AreEqual & overloads instead. + CollectionAssert.Equals se nemá používat pro kontrolní výrazy. Místo toho použijte CollectionAssert.AreEqual & overloads. + + + + CollectionAssert.ReferenceEquals should not be used for Assertions. Please use CollectionAssert methods or Assert.AreSame & overloads instead. + CollectionAssert.ReferenceEquals se nemá používat pro kontrolní výrazy. Místo toho použijte metody CollectionAssert nebo Assert.AreSame & overloads. + diff --git a/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.de.xlf b/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.de.xlf index 651d41e168..db8144a691 100644 --- a/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.de.xlf +++ b/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.de.xlf @@ -37,6 +37,37 @@ Erwartet:<{1} ({2})>. Tatsächlich:<{3} ({4})>. {0} + + {0}{1} +{2} +{3} +{4} + {0}{1} +{2} +{3} +{4} + + + + Expected: + Erwartet: + + + + But was: + War aber: + + + + String lengths are both {0} but differ at index {1}. + Die Zeichenfolgenlängen sind beide {0}, unterscheiden sich jedoch bei Index {1}. + + + + Expected string length {0} but was {1}. + Die erwartete Länge der Zeichenfolge ist {0}, war aber {1}. + + Expected any value except:<{1}>. Actual:<{2}>. {0} Es wurde ein beliebiger Wert erwartet außer:<{1}>. Tatsächlich:<{2}>. {0} @@ -82,14 +113,59 @@ Die Zeichenfolge "{0}" enthält nicht die Zeichenfolge "{1}". {2}. + + Expected collection to contain the specified item. {0} + Es wurde erwartet, dass die Sammlung das angegebene Element enthält. {0} + + + + Expected at least one item to match the predicate. {0} + Es wurde erwartet, dass mindestens ein Element mit dem Prädikat übereinstimmt. {0} + + + + Expected collection to contain exactly one element but found {1} element(s). {0} + Es wurde erwartet, dass die Sammlung genau ein Element enthält, es wurden jedoch {1} gefunden. {0} + + + + Expected exactly one item to match the predicate but found {1} item(s). {0} + Es wurde erwartet, dass genau ein Element mit dem Prädikat übereinstimmt, es wurden jedoch {1} Element(e) gefunden. {0} + + String '{0}' does contain string '{1}'. {2}. Die Zeichenfolge „{0}“ enthält die Zeichenfolge „{1}“. {2}. + + Expected collection to not contain the specified item. {0} + Es wurde erwartet, dass die Sammlung das angegebene Element nicht enthält. {0} + + + + Expected no items to match the predicate. {0} + Es wurden erwartet, dass keine Elemente mit dem Prädikat übereinstimmen. {0} + + + + String '{0}' ends with string '{1}'. {2} + Die Zeichenfolge „{0}“ endet mit der Zeichenfolge „{1}“. {2} + + + + String '{0}' starts with string '{1}'. {2} + Die Zeichenfolge „{0}“ beginnt mit der Zeichenfolge „{1}“. {2} + + + + Dynamic data field '{0}' should be static. + Das dynamische Datenfeld „{0}“ sollte statisch sein. + + - Dynamic data method '{0}' should be static, parameterless and non-generic. - Die dynamische Datenmethode "{0}" muss statisch, parameterlos sein und nichtgenerisch sein. + Dynamic data method '{0}' should be static, non-generic, and cannot have 'params' parameter. + Die dynamische Datenmethode „{0}“ muss statisch, nicht generisch sein und darf keinen Parameter „params“ enthalten. @@ -98,8 +174,8 @@ - The dynamic data source '{0}' in type '{1}' should exist and be a property or a method. - Die dynamische Datenquelle '{0}' im Typ '{1}' muss vorhanden sein und eine Eigenschaft oder Methode sein. + The dynamic data source '{0}' in type '{1}' should exist and be a property, a method, or a field. + Die dynamische Datenquelle „{0}“ vom Typ „{1}“ sollte vorhanden sein und eine Eigenschaft, eine Methode oder ein Feld darstellen. @@ -122,9 +198,9 @@ Tatsächlich: {2} - String '{0}' does not end with string '{1}'. {2}. - Die Zeichenfolge "{0}" endet nicht auf die Zeichenfolge "{1}". {2}. - + String '{0}' does not end with string '{1}'. {2} + Die Zeichenfolge „{0}“ endet nicht auf die Zeichenfolge „{1}“. {2} + {0} failed. {1} @@ -141,15 +217,20 @@ Tatsächlich: {2} Ungültige GitHub-Ticket-URL. + + Value '{0}' is not within the expected range [{1}, {2}]. {3} + Der Wert „{0}“ liegt nicht im erwarteten Bereich [{1}, {2}]. {3} + + {0} Expected type:<{1}>. Actual type:<{2}>. {0}Erwarteter Typ:<{1}>. Tatsächlicher Typ:<{2}>. - String '{0}' does not match pattern '{1}'. {2}. - Die Zeichenfolge "{0}" stimmt nicht mit dem Muster "{1}" überein. {2}. - + String '{0}' does not match pattern '{1}'. {2} + Die Zeichenfolge „{0}“ stimmt nicht mit dem Muster „{1}“ überein. {2} + Expected collection to contain any item but it is empty. {0} @@ -162,14 +243,9 @@ Tatsächlich: {2} - String '{0}' matches pattern '{1}'. {2}. - Die Zeichenfolge "{0}" stimmt mit dem Muster "{1}" überein. {2}. - - - - Assert.Equals should not be used for Assertions. Please use Assert.AreEqual & overloads instead. - Assert.Equals sollte nicht für Assertions verwendet werden. Verwenden Sie stattdessen Assert.AreEqual und Überladungen. - + String '{0}' matches pattern '{1}'. {2} + Die Zeichenfolge „{0}“ stimmt mit dem Muster „{1}“ überein. {2} + @@ -182,7 +258,7 @@ Tatsächlich: {2} oder der Member ist möglicherweise privat und für die Basisklasse definiert. Wenn letzteres zutrifft, müssen Sie den Typ, der den Member definiert, an den Konstruktor von PrivateObject übergeben. - + @@ -195,7 +271,7 @@ Tatsächlich: {2} oder der Member ist möglicherweise privat und für die Basisklasse definiert. Wenn letzteres zutrifft, müssen Sie den Typ, der den Member definiert, an den Konstruktor von PrivateObject übergeben. - + The parameter '{0}' is invalid. The value cannot be null. {1}. @@ -207,10 +283,15 @@ Tatsächlich: {2} Unterschiedliche Anzahl von Elementen. + + Element(s) <{0}> is/are not present in the collection. + Element(s) <{0}> is/are not present in the collection. + + - String '{0}' does not start with string '{1}'. {2}. - Die Zeichenfolge "{0}" beginnt nicht mit der Zeichenfolge "{1}". {2}. - + String '{0}' does not start with string '{1}'. {2} + Die Zeichenfolge „{0}“ beginnt nicht mit der Zeichenfolge „{1}“. {2} + The property {0} has type {1}; expected type {2}. @@ -272,11 +353,6 @@ Tatsächlich: {2} {0} ({1}) - - async TestMethod with UITestMethodAttribute are not supported. Either remove async or use TestMethodAttribute. - "async TestMethod" wird mit UITestMethodAttribute nicht unterstützt. Entfernen Sie "async", oder verwenden Sie TestMethodAttribute. - - Property or method {0} on {1} return type is not assignable to 'IEnumerable'. Eigenschaft oder Methode {0} für {1} Rückgabetyp kann „IEnumerable“ nicht zugewiesen werden. @@ -297,18 +373,65 @@ Tatsächlich: {2} Eigenschaft oder Methode "{0}" in "{1}" gibt leeres IEnumerable<object[]> zurück. - - UITestMethodAttribute.DispatcherQueue should not be null. To use UITestMethodAttribute within a WinUI Desktop App, remember to set the static UITestMethodAttribute.DispatcherQueue during the test initialization. - UITestMethodAttribute.DispatcherQueue darf nicht NULL sein. Um UITestMethodAttribute in einer WinUI-Desktop-App zu verwenden, denken Sie daran, die statische UITestMethodAttribute.DispatcherQueue während der Testinitialisierung festzulegen. + + Actual value <{2}> is not greater than expected value <{1}>. {0} + Der tatsächliche Wert <{2}> ist nicht größer als der erwartete Wert <{1}>. {0} + + + + Actual value <{2}> is not greater than or equal to expected value <{1}>. {0} + Der tatsächliche Wert <{2}> ist nicht größer oder gleich dem erwarteten Wert <{1}>. {0} + + + + Actual value <{2}> is not less than expected value <{1}>. {0} + Der tatsächliche Wert <{2}> ist nicht kleiner als der erwartete Wert <{1}>. {0} + + + + Actual value <{2}> is not less than or equal to expected value <{1}>. {0} + Der tatsächliche Wert <{2}> ist nicht kleiner oder gleich dem erwarteten Wert <{1}>. {0} - - Type '{0}' is not assignable to '{1}'. - Der Typ "{0}" kann nicht "{1}" zugewiesen werden. - - - {0} argument name like "applicationType" - - {1} fully qualified class name like "Microsoft.UI.Xaml.Application" - + + Expected value <{1}> to be positive. {0} + Es wurde erwartet, dass der Wert <{1}> positiv ist. {0} + + + + Expected value <{1}> to be negative. {0} + Es wurde erwartet, dass der Wert <{1}> negativ ist. {0} + + + + Assert.Equals should not be used for Assertions. Please use Assert.AreEqual & overloads instead. + Assert.Equals sollte nicht für Assertions verwendet werden. Verwenden Sie stattdessen Assert.AreEqual und Überladungen. + + + + Assert.ReferenceEquals should not be used for Assertions. Please use Assert.AreSame & overloads instead. + Assert.ReferenceEquals sollte nicht für Assertions verwendet werden. Verwenden Sie stattdessen Assert.AreSame und Überladungen. + + + + StringAssert.Equals should not be used for Assertions. Please use StringAssert methods or Assert.AreEqual & overloads instead. + StringAssert.Equals sollte nicht für Assertions verwendet werden. Verwenden Sie stattdessen StringAssert-Methoden oder Assert.AreEqual und Überladungen. + + + + StringAssert.ReferenceEquals should not be used for Assertions. Please use StringAssert methods or Assert.AreSame & overloads instead. + StringAssert.ReferenceEquals darf nicht für Assertions verwendet werden. Verwenden Sie stattdessen StringAssert-Methoden oder Assert.AreSame und Überladungen. + + + + CollectionAssert.Equals should not be used for Assertions. Please use CollectionAssert.AreEqual & overloads instead. + CollectionAssert.Equals sollte nicht für Assertions verwendet werden. Verwenden Sie stattdessen CollectionAssert.AreEqual und Überladungen. + + + + CollectionAssert.ReferenceEquals should not be used for Assertions. Please use CollectionAssert methods or Assert.AreSame & overloads instead. + CollectionAssert.ReferenceEquals darf nicht für Assertions verwendet werden. Verwenden Sie stattdessen CollectionAssert-Methoden oder Assert.AreSame und Überladungen. + diff --git a/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.es.xlf b/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.es.xlf index f838df72e1..c3c2d018c6 100644 --- a/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.es.xlf +++ b/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.es.xlf @@ -37,6 +37,37 @@ Se esperaba:<{1} ({2})>, pero es:<{3} ({4})>. {0} + + {0}{1} +{2} +{3} +{4} + {0}{1} +{2} +{3} +{4} + + + + Expected: + Se esperaba: + + + + But was: + Pero fue: + + + + String lengths are both {0} but differ at index {1}. + Las longitudes de las cadenas son {0} pero difieren en el índice {1}. + + + + Expected string length {0} but was {1}. + Se esperaba una longitud de cadena {0} pero fue {1}. + + Expected any value except:<{1}>. Actual:<{2}>. {0} Se esperaba cualquier valor excepto <{1}>, pero es <{2}>. {0} @@ -82,14 +113,59 @@ La cadena '{0}' no contiene la cadena '{1}'. {2}. + + Expected collection to contain the specified item. {0} + Se esperaba que la colección contuviera el elemento especificado. {0} + + + + Expected at least one item to match the predicate. {0} + Se esperaba al menos un elemento para que coincida con el predicado. {0} + + + + Expected collection to contain exactly one element but found {1} element(s). {0} + Se esperaba que la colección contuviera exactamente un elemento, pero se encontraron {1} elementos. {0} + + + + Expected exactly one item to match the predicate but found {1} item(s). {0} + Se esperaba exactamente un elemento para que coincida con el predicado, pero se encontraron {1} elementos. {0} + + String '{0}' does contain string '{1}'. {2}. La cadena '{0}' contiene la cadena '{1}'. {2}. + + Expected collection to not contain the specified item. {0} + Se esperaba que la colección no contenga el elemento especificado. {0} + + + + Expected no items to match the predicate. {0} + No se esperaba que ningún elemento coincidía con el predicado. {0} + + + + String '{0}' ends with string '{1}'. {2} + La cadena "{0}" termina con la cadena "{1}". {2} + + + + String '{0}' starts with string '{1}'. {2} + La cadena "{0}" comienza con la cadena "{1}". {2} + + + + Dynamic data field '{0}' should be static. + El campo de datos dinámico '{0}' debe ser estático. + + - Dynamic data method '{0}' should be static, parameterless and non-generic. - El método de datos dinámicos '{0}' debe ser estático, sin parámetros y no genérico. + Dynamic data method '{0}' should be static, non-generic, and cannot have 'params' parameter. + El método de datos dinámicos '{0}' debe ser estático, no genérico y no puede tener un parámetro 'params'. @@ -98,8 +174,8 @@ - The dynamic data source '{0}' in type '{1}' should exist and be a property or a method. - El origen de datos dinámico '{0}' en el tipo '{1}' debe existir y ser una propiedad o un método. + The dynamic data source '{0}' in type '{1}' should exist and be a property, a method, or a field. + El origen de datos dinámico '{0}' del tipo '{1}' debe existir y ser una propiedad, un método o un campo. @@ -122,9 +198,9 @@ Real: {2} - String '{0}' does not end with string '{1}'. {2}. - La cadena '{0}' no termina con la cadena '{1}'. {2}. - + String '{0}' does not end with string '{1}'. {2} + La cadena "{0}" no termina con la cadena "{1}". {2} + {0} failed. {1} @@ -141,15 +217,20 @@ Real: {2} Dirección URL de vale de GitHub no válida + + Value '{0}' is not within the expected range [{1}, {2}]. {3} + El valor "{0}" no está dentro del rango esperado [{1}, {2}]. {3} + + {0} Expected type:<{1}>. Actual type:<{2}>. {0} Tipo esperado:<{1}>. Tipo real:<{2}>. - String '{0}' does not match pattern '{1}'. {2}. - La cadena '{0}' no coincide con el patrón '{1}'. {2}. - + String '{0}' does not match pattern '{1}'. {2} + La cadena "{0}" no coincide con el patrón "{1}". {2} + Expected collection to contain any item but it is empty. {0} @@ -162,14 +243,9 @@ Real: {2} - String '{0}' matches pattern '{1}'. {2}. - La cadena '{0}' coincide con el patrón '{1}'. {2}. - - - - Assert.Equals should not be used for Assertions. Please use Assert.AreEqual & overloads instead. - No se debe usar Assert.Equals para aserciones. Use Assert.AreEqual y Overloads en su lugar. - + String '{0}' matches pattern '{1}'. {2} + La cadena "{0}" coincide con el patrón "{1}". {2} + @@ -182,7 +258,7 @@ Real: {2} o que el miembro sea privado y esté definido en una clase base. Si esto último es cierto, debe pasar el tipo que define el miembro al constructor de PrivateObject. - + @@ -195,7 +271,7 @@ Real: {2} o que el miembro sea privado y esté definido en una clase base. Si esto último es cierto, debe pasar el tipo que define el miembro al constructor de PrivateObject. - + The parameter '{0}' is invalid. The value cannot be null. {1}. @@ -207,10 +283,15 @@ Real: {2} Número diferente de elementos. + + Element(s) <{0}> is/are not present in the collection. + Element(s) <{0}> is/are not present in the collection. + + - String '{0}' does not start with string '{1}'. {2}. - La cadena '{0}' no empieza con la cadena '{1}'. {2}. - + String '{0}' does not start with string '{1}'. {2} + La cadena "{0}" no empieza con la cadena "{1}". {2} + The property {0} has type {1}; expected type {2}. @@ -272,11 +353,6 @@ Real: {2} {0} ({1}) - - async TestMethod with UITestMethodAttribute are not supported. Either remove async or use TestMethodAttribute. - async TestMethod con UITestMethodAttribute no son compatibles. Quite async o use TestMethodAttribute. - - Property or method {0} on {1} return type is not assignable to 'IEnumerable'. La propiedad o el método {0} en el tipo de retorno {1} no se puede asignar a "IEnumerable". @@ -297,18 +373,65 @@ Real: {2} La propiedad o el método {0} en {1} devuelve un elemento IEnumerable<object[]> vacío. - - UITestMethodAttribute.DispatcherQueue should not be null. To use UITestMethodAttribute within a WinUI Desktop App, remember to set the static UITestMethodAttribute.DispatcherQueue during the test initialization. - UITestMethodAttribute.DispatcherQueue no debe ser null. Para usar UITestMethodAttribute en una aplicación de escritorio WinUI, recuerde establecer el UITestMethodAttribute.DispatcherQueue estático durante la inicialización de la prueba. + + Actual value <{2}> is not greater than expected value <{1}>. {0} + El valor real <{2}> no es mayor que el valor esperado <{1}>. {0} + + + + Actual value <{2}> is not greater than or equal to expected value <{1}>. {0} + El valor real <{2}> no es mayor o igual que el valor esperado <{1}>. {0} + + + + Actual value <{2}> is not less than expected value <{1}>. {0} + El valor real <{2}> no es menor que el valor esperado <{1}>. {0} + + + + Actual value <{2}> is not less than or equal to expected value <{1}>. {0} + El valor real <{2}> no es menor o igual que el valor esperado <{1}>. {0} - - Type '{0}' is not assignable to '{1}'. - Tipo "{0}" no se puede asignar a "{1}". - - - {0} argument name like "applicationType" - - {1} fully qualified class name like "Microsoft.UI.Xaml.Application" - + + Expected value <{1}> to be positive. {0} + Se esperaba que el valor <{1}> ser positivo. {0} + + + + Expected value <{1}> to be negative. {0} + Se esperaba que el valor <{1}> ser negativo. {0} + + + + Assert.Equals should not be used for Assertions. Please use Assert.AreEqual & overloads instead. + No se debe usar Assert.Equals para aserciones. Use Assert.AreEqual y Overloads en su lugar. + + + + Assert.ReferenceEquals should not be used for Assertions. Please use Assert.AreSame & overloads instead. + Assert.ReferenceEquals no se debe usar para las aserciones. Use Assert.AreSame & sobrecargas en su lugar. + + + + StringAssert.Equals should not be used for Assertions. Please use StringAssert methods or Assert.AreEqual & overloads instead. + StringAssert.Equals no se debe usar para las aserciones. Use los métodos StringAssert o las sobrecargas Assert.AreEqual & en su lugar. + + + + StringAssert.ReferenceEquals should not be used for Assertions. Please use StringAssert methods or Assert.AreSame & overloads instead. + StringAssert.ReferenceEquals no se debe usar para las aserciones. Use los métodos StringAssert o las sobrecargas Assert.AreSame & en su lugar. + + + + CollectionAssert.Equals should not be used for Assertions. Please use CollectionAssert.AreEqual & overloads instead. + CollectionAssert.Equals no se debe usar para las aserciones. Use CollectionAssert.AreEqual y sobrecargas en su lugar. + + + + CollectionAssert.ReferenceEquals should not be used for Assertions. Please use CollectionAssert methods or Assert.AreSame & overloads instead. + CollectionAssert.ReferenceEquals no se debe usar para las aserciones. Use los métodos CollectionAssert o las sobrecargas Assert.AreSame & en su lugar. + diff --git a/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.fr.xlf b/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.fr.xlf index c342c620e5..4f1d8fd807 100644 --- a/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.fr.xlf +++ b/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.fr.xlf @@ -37,6 +37,37 @@ Attendu : <{1} ({2})>, Réel : <{3} ({4})>. {0} + + {0}{1} +{2} +{3} +{4} + {0}{1} +{2} +{3} +{4} + + + + Expected: + Attendu : + + + + But was: + Mais c'était : + + + + String lengths are both {0} but differ at index {1}. + Les longueurs de chaîne sont toutes les deux {0} mais diffèrent à l’index {1}. + + + + Expected string length {0} but was {1}. + La longueur de chaîne attendue {0} mais était {1}. + + Expected any value except:<{1}>. Actual:<{2}>. {0} Toute valeur attendue sauf :<{1}>. Réel :<{2}>. {0} @@ -82,14 +113,59 @@ La chaîne '{0}' ne contient pas la chaîne '{1}'. {2}. + + Expected collection to contain the specified item. {0} + Collection attendue pour contenir l’élément spécifié. {0} + + + + Expected at least one item to match the predicate. {0} + Au moins un élément doit correspondre au prédicat. {0} + + + + Expected collection to contain exactly one element but found {1} element(s). {0} + Collection attendue pour contenir exactement un élément mais {1} élément(s) trouvé(s). {0} + + + + Expected exactly one item to match the predicate but found {1} item(s). {0} + Un seul élément était attendu pour correspondre au prédicat, mais {1} élément(s) trouvé(s). {0} + + String '{0}' does contain string '{1}'. {2}. La chaîne « {0} » contient la chaîne « {1} ». {2}. + + Expected collection to not contain the specified item. {0} + Collection attendue pour ne pas contenir l’élément spécifié. {0} + + + + Expected no items to match the predicate. {0} + Aucun élément ne doit correspondre au prédicat. {0} + + + + String '{0}' ends with string '{1}'. {2} + La chaîne '{0}' se termine par la chaîne '{1}'. {2} + + + + String '{0}' starts with string '{1}'. {2} + La chaîne '{0}' commence par la chaîne '{1}'. {2} + + + + Dynamic data field '{0}' should be static. + Le champ de données dynamiques « {0} » doit être statique. + + - Dynamic data method '{0}' should be static, parameterless and non-generic. - La méthode de données dynamiques « {0} » doit être statique, sans paramètre et non générique. + Dynamic data method '{0}' should be static, non-generic, and cannot have 'params' parameter. + La méthode de données dynamiques « {0} » doit être statique, non générique et ne peut pas avoir le paramètre « params ». @@ -98,8 +174,8 @@ - The dynamic data source '{0}' in type '{1}' should exist and be a property or a method. - La source de données dynamique '{0}' dans le type '{1}' doit exister et être une propriété ou une méthode. + The dynamic data source '{0}' in type '{1}' should exist and be a property, a method, or a field. + La source de données dynamique « {0} » du type « {1} » doit exister et être une propriété, une méthode ou un champ. @@ -122,9 +198,9 @@ Réel : {2} - String '{0}' does not end with string '{1}'. {2}. - La chaîne '{0}' ne se termine pas par la chaîne '{1}'. {2}. - + String '{0}' does not end with string '{1}'. {2} + La chaîne '{0}' ne se termine pas par la chaîne '{1}'. {2} + {0} failed. {1} @@ -141,15 +217,20 @@ Réel : {2} URL de ticket GitHub non valide + + Value '{0}' is not within the expected range [{1}, {2}]. {3} + La valeur « {0} » n'est pas dans la plage attendue [{1}, {2}]. {3} + + {0} Expected type:<{1}>. Actual type:<{2}>. {0}Type attendu :<{1}>. Type réel :<{2}>. - String '{0}' does not match pattern '{1}'. {2}. - La chaîne '{0}' ne correspond pas au modèle '{1}'. {2}. - + String '{0}' does not match pattern '{1}'. {2} + La chaîne '{0}' ne correspond pas au modèle '{1}'. {2} + Expected collection to contain any item but it is empty. {0} @@ -162,14 +243,9 @@ Réel : {2} - String '{0}' matches pattern '{1}'. {2}. - La chaîne '{0}' correspond au modèle '{1}'. {2}. - - - - Assert.Equals should not be used for Assertions. Please use Assert.AreEqual & overloads instead. - Assert.Equals ne doit pas être utilisé pour les assertions. Utilisez Assert.AreEqual & overloads à la place. - + String '{0}' matches pattern '{1}'. {2} + La chaîne '{0}' correspond au modèle '{1}'. {2} + @@ -182,7 +258,7 @@ Réel : {2} ou le membre est peut-être private et défini sur une classe de base. Si le dernier cas est vrai, vous devez transmettre le type qui définit le membre dans le constructeur de PrivateObject. - + @@ -195,7 +271,7 @@ Réel : {2} ou le membre est peut-être private et défini sur une classe de base. Si le dernier cas est vrai, vous devez transmettre le type qui définit le membre dans le constructeur de PrivateObject. - + The parameter '{0}' is invalid. The value cannot be null. {1}. @@ -207,10 +283,15 @@ Réel : {2} Nombre d'éléments différent. + + Element(s) <{0}> is/are not present in the collection. + Element(s) <{0}> is/are not present in the collection. + + - String '{0}' does not start with string '{1}'. {2}. - La chaîne '{0}' ne commence pas par la chaîne '{1}'. {2}. - + String '{0}' does not start with string '{1}'. {2} + La chaîne '{0}' ne commence pas par la chaîne '{1}'. {2} + The property {0} has type {1}; expected type {2}. @@ -272,11 +353,6 @@ Réel : {2} {0} ({1}) - - async TestMethod with UITestMethodAttribute are not supported. Either remove async or use TestMethodAttribute. - async TestMethod avec UITestMethodAttribute ne sont pas pris en charge. Supprimez async ou utilisez TestMethodAttribute. - - Property or method {0} on {1} return type is not assignable to 'IEnumerable'. La propriété ou la méthode {0} sur le type de retour {1} ne peut pas être attribuée à « IEnumerable ». @@ -297,18 +373,65 @@ Réel : {2} La propriété ou la méthode {0} sur {1} retourne un IEnumerable<object[]> vide. - - UITestMethodAttribute.DispatcherQueue should not be null. To use UITestMethodAttribute within a WinUI Desktop App, remember to set the static UITestMethodAttribute.DispatcherQueue during the test initialization. - UITestMethodAttribute.DispatcherQueue ne doit pas avoir la valeur nul. Pour utiliser UITestMethodAttribute dans une application de bureau WinUI, n’oubliez pas de définir l’UITestMethodAttribute.DispatcherQueue statique pendant l’initialisation du test. + + Actual value <{2}> is not greater than expected value <{1}>. {0} + La valeur réelle <{2}> n’est pas supérieure à la valeur attendue <{1}>. {0} + + + + Actual value <{2}> is not greater than or equal to expected value <{1}>. {0} + La valeur réelle <{2}> n’est pas supérieure ou égale à la valeur attendue <{1}>. {0} + + + + Actual value <{2}> is not less than expected value <{1}>. {0} + La valeur réelle <{2}> n’est pas inférieure à la valeur attendue <{1}>. {0} + + + + Actual value <{2}> is not less than or equal to expected value <{1}>. {0} + La valeur réelle <{2}> n’est pas inférieure ou égale à la valeur attendue <{1}>. {0} - - Type '{0}' is not assignable to '{1}'. - Impossible d'assigner le type '{0}' à '{1}'. - - - {0} argument name like "applicationType" - - {1} fully qualified class name like "Microsoft.UI.Xaml.Application" - + + Expected value <{1}> to be positive. {0} + La valeur attendue <{1}> doit être positive. {0} + + + + Expected value <{1}> to be negative. {0} + La valeur attendue <{1}> doit être négative. {0} + + + + Assert.Equals should not be used for Assertions. Please use Assert.AreEqual & overloads instead. + Assert.Equals ne doit pas être utilisé pour les assertions. Utilisez Assert.AreEqual & overloads à la place. + + + + Assert.ReferenceEquals should not be used for Assertions. Please use Assert.AreSame & overloads instead. + Assert.ReferenceEquals ne doit pas être utilisé pour les assertions. Utilisez Assert.AreEqual & overloads à la place. + + + + StringAssert.Equals should not be used for Assertions. Please use StringAssert methods or Assert.AreEqual & overloads instead. + StringAssert.Equals ne doit pas être utilisé pour les assertions. Utilisez plutôt les méthodes StringAssert ou Assert.AreEqual & overloads. + + + + StringAssert.ReferenceEquals should not be used for Assertions. Please use StringAssert methods or Assert.AreSame & overloads instead. + StringAssert.ReferenceEquals ne doit pas être utilisé pour les assertions. Utilisez plutôt les méthodes StringAssert ou Assert.AreSame & overloads. + + + + CollectionAssert.Equals should not be used for Assertions. Please use CollectionAssert.AreEqual & overloads instead. + Assert.Equals ne doit pas être utilisé pour les assertions. Utilisez plutôt CollectionAssert.AreEqual & overloads. + + + + CollectionAssert.ReferenceEquals should not be used for Assertions. Please use CollectionAssert methods or Assert.AreSame & overloads instead. + CollectionAssert.ReferenceEquals ne doit pas être utilisé pour les assertions. Utilisez plutôt les méthodes CollectionAssert ou Assert.AreSame & overloads. + diff --git a/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.it.xlf b/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.it.xlf index 3b297c7af2..d28a976988 100644 --- a/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.it.xlf +++ b/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.it.xlf @@ -37,6 +37,37 @@ Previsto:<{1} ({2})>. Effettivo:<{3} ({4})>. {0} + + {0}{1} +{2} +{3} +{4} + {0}{1} +{2} +{3} +{4} + + + + Expected: + Previsto: + + + + But was: + Ma era: + + + + String lengths are both {0} but differ at index {1}. + Le lunghezze delle stringhe sono entrambe {0} ma differiscono all'indice {1}. + + + + Expected string length {0} but was {1}. + La lunghezza della stringa prevista è {0} ma era {1}. + + Expected any value except:<{1}>. Actual:<{2}>. {0} Previsto qualsiasi valore tranne:<{1}>. Effettivo:<{2}>. {0} @@ -82,14 +113,59 @@ La stringa '{0}' non contiene la stringa '{1}'. {2}. + + Expected collection to contain the specified item. {0} + La raccolta dovrebbe contenere l'elemento specificato. {0} + + + + Expected at least one item to match the predicate. {0} + Almeno un elemento dovrebbe corrispondere al predicato. {0} + + + + Expected collection to contain exactly one element but found {1} element(s). {0} + La raccolta dovrebbe contenere un unico elemento, ma ne sono stati trovati {1}. {0} + + + + Expected exactly one item to match the predicate but found {1} item(s). {0} + Un unico elemento dovrebbe corrispondere al predicato, ma ne sono stati trovati {1}. {0} + + String '{0}' does contain string '{1}'. {2}. La stringa '{0}' contiene la stringa '{1}'. {2}. + + Expected collection to not contain the specified item. {0} + La raccolta non dovrebbe contenere l'elemento specificato. {0} + + + + Expected no items to match the predicate. {0} + Nessun elemento dovrebbe corrispondere al predicato. {0} + + + + String '{0}' ends with string '{1}'. {2} + La stringa '{0}' termina con la stringa '{1}'. {2} + + + + String '{0}' starts with string '{1}'. {2} + La stringa '{0}' inizia con la stringa '{1}'. {2} + + + + Dynamic data field '{0}' should be static. + Il campo dati dinamico '{0}' deve essere statico. + + - Dynamic data method '{0}' should be static, parameterless and non-generic. - Il metodo '{0}' di Dynamic Data deve essere statico, senza parametri e non generico. + Dynamic data method '{0}' should be static, non-generic, and cannot have 'params' parameter. + Il metodo dei dati dinamici '{0}' deve essere statico, non generico e non può includere un parametro 'params'. @@ -98,8 +174,8 @@ - The dynamic data source '{0}' in type '{1}' should exist and be a property or a method. - L'origine dati dinamica '{0}' nel tipo '{1}' deve esistere ed essere una proprietà o un metodo. + The dynamic data source '{0}' in type '{1}' should exist and be a property, a method, or a field. + L'origine dati dinamica '{0}' nel tipo '{1}' deve esistere ed essere una proprietà, un metodo o un campo. @@ -122,9 +198,9 @@ Effettivo: {2} - String '{0}' does not end with string '{1}'. {2}. - La stringa '{0}' non termina con la stringa '{1}'. {2}. - + String '{0}' does not end with string '{1}'. {2} + La stringa '{0}' non termina con la stringa '{1}'. {2} + {0} failed. {1} @@ -141,15 +217,20 @@ Effettivo: {2} L'URL del ticket GitHub non è valido + + Value '{0}' is not within the expected range [{1}, {2}]. {3} + Il valore '{0}' non è compreso nell'intervallo previsto [{1}, {2}]. {3} + + {0} Expected type:<{1}>. Actual type:<{2}>. {0} Tipo previsto:<{1}>. Tipo effettivo:<{2}>. - String '{0}' does not match pattern '{1}'. {2}. - La stringa '{0}' non corrisponde al criterio '{1}'. {2}. - + String '{0}' does not match pattern '{1}'. {2} + La stringa '{0}' non corrisponde al criterio '{1}'. {2} + Expected collection to contain any item but it is empty. {0} @@ -162,14 +243,9 @@ Effettivo: {2} - String '{0}' matches pattern '{1}'. {2}. - La stringa '{0}' corrisponde al criterio '{1}'. {2}. - - - - Assert.Equals should not be used for Assertions. Please use Assert.AreEqual & overloads instead. - Non è possibile usare Assert.Equals per le asserzioni. Usare Assert.AreEqual e gli overload. - + String '{0}' matches pattern '{1}'. {2} + La stringa '{0}' corrisponde al criterio '{1}'. {2} + @@ -182,7 +258,7 @@ Effettivo: {2} oppure il membro potrebbe essere privato e definito per una classe base. In quest'ultimo caso, è necessario passare il tipo che definisce il membro nel costruttore di PrivateObject. - + @@ -195,7 +271,7 @@ Effettivo: {2} oppure il membro potrebbe essere privato e definito per una classe base. In quest'ultimo caso, è necessario passare il tipo che definisce il membro nel costruttore di PrivateObject. - + The parameter '{0}' is invalid. The value cannot be null. {1}. @@ -207,10 +283,15 @@ Effettivo: {2} Il numero di elementi è diverso. + + Element(s) <{0}> is/are not present in the collection. + Element(s) <{0}> is/are not present in the collection. + + - String '{0}' does not start with string '{1}'. {2}. - La stringa '{0}' non inizia con la stringa '{1}'. {2}. - + String '{0}' does not start with string '{1}'. {2} + La stringa '{0}' non inizia con la stringa '{1}'. {2} + The property {0} has type {1}; expected type {2}. @@ -272,11 +353,6 @@ Effettivo: {2} {0} ({1}) - - async TestMethod with UITestMethodAttribute are not supported. Either remove async or use TestMethodAttribute. - L'elemento async TestMethod con UITestMethodAttribute non è supportato. Rimuovere async o usare TestMethodAttribute. - - Property or method {0} on {1} return type is not assignable to 'IEnumerable'. La proprietà o il metodo {0} su {1} tipo restituito non è assegnabile a 'IEnumerable'. @@ -297,18 +373,65 @@ Effettivo: {2} La proprietà o il metodo {0} nella classe {1} restituisce un elemento IEnumerable<object[]> vuoto. - - UITestMethodAttribute.DispatcherQueue should not be null. To use UITestMethodAttribute within a WinUI Desktop App, remember to set the static UITestMethodAttribute.DispatcherQueue during the test initialization. - UITestMethodAttribute.DispatcherQueue non deve essere Null. Per usare UITestMethodAttribute all'interno di un'app desktop WinUI, ricordarsi di impostare il parametro statico uiTestMethodAttribute.DispatcherQueue durante l'inizializzazione del test. + + Actual value <{2}> is not greater than expected value <{1}>. {0} + Il valore effettivo <{2}> non è maggiore del valore previsto <{1}>. {0} + + + + Actual value <{2}> is not greater than or equal to expected value <{1}>. {0} + Il valore effettivo <{2}> non è maggiore o uguale al valore previsto <{1}>. {0} + + + + Actual value <{2}> is not less than expected value <{1}>. {0} + Il valore effettivo <{2}> non è minore del valore previsto <{1}>. {0} + + + + Actual value <{2}> is not less than or equal to expected value <{1}>. {0} + Il valore effettivo <{2}> non è minore o uguale al valore previsto <{1}>. {0} - - Type '{0}' is not assignable to '{1}'. - Il tipo '{0}' non è assegnabile a '{1}'. - - - {0} argument name like "applicationType" - - {1} fully qualified class name like "Microsoft.UI.Xaml.Application" - + + Expected value <{1}> to be positive. {0} + Il valore <{1}{0}> dovrebbe essere positivo. + + + + Expected value <{1}> to be negative. {0} + Il valore <{1}{0}> dovrebbe essere negativo. + + + + Assert.Equals should not be used for Assertions. Please use Assert.AreEqual & overloads instead. + Non è possibile usare Assert.Equals per le asserzioni. Usare Assert.AreEqual e gli overload. + + + + Assert.ReferenceEquals should not be used for Assertions. Please use Assert.AreSame & overloads instead. + Non è possibile usare Assert.ReferenceEquals per le asserzioni. In alternativa, usare Assert.AreSame e gli overload. + + + + StringAssert.Equals should not be used for Assertions. Please use StringAssert methods or Assert.AreEqual & overloads instead. + Non è possibile usare StringAssert.Equals per le asserzioni. In alternativa, usare i metodi StringAssert o Assert.AreEqual e gli overload. + + + + StringAssert.ReferenceEquals should not be used for Assertions. Please use StringAssert methods or Assert.AreSame & overloads instead. + Non è possibile usare StringAssert.ReferenceEquals per le asserzioni. In alternativa, usare i metodi StringAssert o Assert.AreSame e gli overload. + + + + CollectionAssert.Equals should not be used for Assertions. Please use CollectionAssert.AreEqual & overloads instead. + Non è possibile usare CollectionAssert.Equals per le asserzioni. In alternativa, usare CollectionAssert.AreEqual e gli overload. + + + + CollectionAssert.ReferenceEquals should not be used for Assertions. Please use CollectionAssert methods or Assert.AreSame & overloads instead. + Non è possibile usare CollectionAssert.ReferenceEquals per le asserzioni. In alternativa, usare i metodi CollectionAssert o Assert.AreSame e gli overload. + diff --git a/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.ja.xlf b/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.ja.xlf index ad0c99078d..9d957951d5 100644 --- a/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.ja.xlf +++ b/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.ja.xlf @@ -37,6 +37,37 @@ <{1} ({2})> が必要ですが、<{3} ({4})> が指定されました。{0} + + {0}{1} +{2} +{3} +{4} + {0}{1} +{2} +{3} +{4} + + + + Expected: + 期待値: + + + + But was: + しかし、次でした: + + + + String lengths are both {0} but differ at index {1}. + 文字列の長さは両方とも {0} ですが、インデックス {1} で異なります。 + + + + Expected string length {0} but was {1}. + 期待される文字列の長さは {0} ですが、実際は {1} でした。 + + Expected any value except:<{1}>. Actual:<{2}>. {0} <{1}> 以外の任意の値が必要ですが、<{2}> が指定されています。{0} @@ -82,14 +113,59 @@ 文字列 '{0}' は文字列 '{1}' を含んでいません。{2}。 + + Expected collection to contain the specified item. {0} + 指定された項目を含むコレクションが必要です。{0} + + + + Expected at least one item to match the predicate. {0} + 述語と一致する項目が少なくとも 1 つ必要です。{0} + + + + Expected collection to contain exactly one element but found {1} element(s). {0} + コレクションには 1 つの要素だけを含める必要がありますが、{1} 要素が見つかりました。{0} + + + + Expected exactly one item to match the predicate but found {1} item(s). {0} + 述語と一致する項目が 1 つだけ必要ですが、{1} 項目が見つかりました。{0} + + String '{0}' does contain string '{1}'. {2}. 文字列 '{0}' は文字列 '{1}' を含んでいます。{2}。 + + Expected collection to not contain the specified item. {0} + 指定された項目を含まないコレクションが必要です。{0} + + + + Expected no items to match the predicate. {0} + 述語に一致する項目が必要ありません。{0} + + + + String '{0}' ends with string '{1}'. {2} + 文字列 '{0}' の末尾は文字列 '{1}' です。{2} + + + + String '{0}' starts with string '{1}'. {2} + 文字列 '{0}' は文字列 '{1}' で始まります。 {2} + + + + Dynamic data field '{0}' should be static. + 動的データ フィールド '{0}' は静的である必要があります。 + + - Dynamic data method '{0}' should be static, parameterless and non-generic. - 動的データ メソッド '{0}' は、静的、パラメーターなし、および非ジェネリックである必要があります。 + Dynamic data method '{0}' should be static, non-generic, and cannot have 'params' parameter. + 動的データ メソッド '{0}' は、静的で、非ジェネリックである必要があり、'params' パラメーターを持つことはできません。 @@ -98,8 +174,8 @@ - The dynamic data source '{0}' in type '{1}' should exist and be a property or a method. - 型 '{1}' の動的データ ソース '{0}' は、プロパティまたはメソッドである必要があります。 + The dynamic data source '{0}' in type '{1}' should exist and be a property, a method, or a field. + 型 '{1}' の動的データ ソース '{0}' が、プロパティ、メソッド、またはフィールドとして存在している必要があります。 @@ -122,9 +198,9 @@ Actual: {2} - String '{0}' does not end with string '{1}'. {2}. - 文字列 '{0}' は文字列 '{1}' で終わりません。{2}。 - + String '{0}' does not end with string '{1}'. {2} + 文字列 '{0}' は文字列 '{1}' で終わりません。{2} + {0} failed. {1} @@ -141,15 +217,20 @@ Actual: {2} GitHub チケット URL が無効です + + Value '{0}' is not within the expected range [{1}, {2}]. {3} + 値 '{0}' は予期される範囲 [{1}, {2}] にありません。 {3} + + {0} Expected type:<{1}>. Actual type:<{2}>. {0} には型 <{1}> が必要ですが、型 <{2}> が指定されました。 - String '{0}' does not match pattern '{1}'. {2}. - 文字列 '{0}' はパターン '{1}' と一致しません。{2}。 - + String '{0}' does not match pattern '{1}'. {2} + 文字列 '{0}' はパターン '{1}' と一致しません。{2} + Expected collection to contain any item but it is empty. {0} @@ -162,14 +243,9 @@ Actual: {2} - String '{0}' matches pattern '{1}'. {2}. - 文字列 '{0}' はパターン '{1}' と一致します。{2}。 - - - - Assert.Equals should not be used for Assertions. Please use Assert.AreEqual & overloads instead. - アサーションには Assert.Equals を使用せずに、Assert.AreEqual とオーバーロードを使用してください。(& ) - + String '{0}' matches pattern '{1}'. {2} + 文字列 '{0}' はパターン '{1}' と一致します。{2} + @@ -182,7 +258,7 @@ Actual: {2} またはメンバーがプライベートであり、基底クラスで定義されている可能性があります。後者である場合は、メンバーを 定義する型を PrivateObject のコンストラクターに渡す必要があります。 - + @@ -195,7 +271,7 @@ Actual: {2} またはメンバーがプライベートであり、基底クラスで定義されている可能性があります。後者である場合は、メンバーを 定義する型を PrivateObject のコンストラクターに渡す必要があります。 - + The parameter '{0}' is invalid. The value cannot be null. {1}. @@ -207,10 +283,15 @@ Actual: {2} 要素数が異なります。 + + Element(s) <{0}> is/are not present in the collection. + Element(s) <{0}> is/are not present in the collection. + + - String '{0}' does not start with string '{1}'. {2}. - 文字列 '{0}' は文字列 '{1}' で始まりません。{2}。 - + String '{0}' does not start with string '{1}'. {2} + 文字列 '{0}' は文字列 '{1}' で始まりません。{2} + The property {0} has type {1}; expected type {2}. @@ -272,11 +353,6 @@ Actual: {2} {0} ({1}) - - async TestMethod with UITestMethodAttribute are not supported. Either remove async or use TestMethodAttribute. - UITestMethodAttribute が指定された非同期の TestMethod はサポートされていません。非同期を削除するか、TestMethodAttribute を使用してください。 - - Property or method {0} on {1} return type is not assignable to 'IEnumerable'. {1} 戻り値の型のプロパティまたはメソッド {0} は、'IEnumerable' に割り当てできません。 @@ -297,18 +373,65 @@ Actual: {2} {1} 上のプロパティまたはメソッド {0} は空の IEnumerable<object[]> を返します。 - - UITestMethodAttribute.DispatcherQueue should not be null. To use UITestMethodAttribute within a WinUI Desktop App, remember to set the static UITestMethodAttribute.DispatcherQueue during the test initialization. - UITestMethodAttribute.DispatcherQueue を null にすることはできません。WinUI デスクトップ アプリ内で UITestMethodAttribute を使用するには、テストの初期化中に静的な UITestMethodAttribute.DispatcherQueue を設定してください。 + + Actual value <{2}> is not greater than expected value <{1}>. {0} + 実際の値 <{2}> は、予期された値 <{1}> より大きくありません。{0} + + + + Actual value <{2}> is not greater than or equal to expected value <{1}>. {0} + 実際の値 <{2}> は、予期された値 <{1}> 以上ではありません。{0} + + + + Actual value <{2}> is not less than expected value <{1}>. {0} + 実際の値 <{2}> は、予期された値 <{1}> より小さくありません。{0} + + + + Actual value <{2}> is not less than or equal to expected value <{1}>. {0} + 実際の値 <{2}> は、予期された値 <{1}> 以下ではありません。{0} - - Type '{0}' is not assignable to '{1}'. - 型 '{0}' を '{1}' に割り当てることはできません。 - - - {0} argument name like "applicationType" - - {1} fully qualified class name like "Microsoft.UI.Xaml.Application" - + + Expected value <{1}> to be positive. {0} + 正の値 <{1}> が必要です。{0} + + + + Expected value <{1}> to be negative. {0} + 負の値 <{1}> が必要です。{0} + + + + Assert.Equals should not be used for Assertions. Please use Assert.AreEqual & overloads instead. + アサーションには Assert.Equals を使用せずに、Assert.AreEqual とオーバーロードを使用してください。(& ) + + + + Assert.ReferenceEquals should not be used for Assertions. Please use Assert.AreSame & overloads instead. + アサーションには Assert.ReferenceEquals を使用しないでください。Assert.AreSame およびオーバーロードを使用してください。 + + + + StringAssert.Equals should not be used for Assertions. Please use StringAssert methods or Assert.AreEqual & overloads instead. + アサーションには StringAssert.Equals を使用しないでください。代わりに StringAssert メソッドまたは Assert.AreEqual およびオーバーロードを使用してください。 + + + + StringAssert.ReferenceEquals should not be used for Assertions. Please use StringAssert methods or Assert.AreSame & overloads instead. + アサーションには StringAssert.ReferenceEquals を使用しないでください。代わりに StringAssert メソッドまたは Assert.AreSame およびオーバーロードを使用してください。 + + + + CollectionAssert.Equals should not be used for Assertions. Please use CollectionAssert.AreEqual & overloads instead. + アサーションには CollectionAssert.Equals を使用しないでください。代わりに CollectionAssert.AreEqual およびオーバーロードを使用してください。 + + + + CollectionAssert.ReferenceEquals should not be used for Assertions. Please use CollectionAssert methods or Assert.AreSame & overloads instead. + アサーションには CollectionAssert.ReferenceEquals を使用しないでください。代わりに CollectionAssert メソッドまたは Assert.AreSame およびオーバーロードを使用してください。 + diff --git a/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.ko.xlf b/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.ko.xlf index e0428e081c..2445d7f5ff 100644 --- a/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.ko.xlf +++ b/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.ko.xlf @@ -37,6 +37,37 @@ 예상 값: <{1} ({2})>. 실제 값: <{3} ({4})>. {0} + + {0}{1} +{2} +{3} +{4} + {0}{1} +{2} +{3} +{4} + + + + Expected: + 예상: + + + + But was: + 그러나 다음과 같습니다. + + + + String lengths are both {0} but differ at index {1}. + 문자열 길이는 둘 다 {0} 이지만 인덱스 {1}에서 다릅니다. + + + + Expected string length {0} but was {1}. + 문자열 길이 {0}(을)를 예상했지만 {1}입니다. + + Expected any value except:<{1}>. Actual:<{2}>. {0} 예상 값: <{1}>을(를) 제외한 모든 값. 실제 값: <{2}>. {0} @@ -82,14 +113,59 @@ '{0}' 문자열이 '{1}' 문자열을 포함하지 않습니다. {2} + + Expected collection to contain the specified item. {0} + 지정한 항목을 포함할 컬렉션이 필요합니다. {0} + + + + Expected at least one item to match the predicate. {0} + 조건자와 일치하는 항목이 하나 이상 필요합니다. {0} + + + + Expected collection to contain exactly one element but found {1} element(s). {0} + 정확히 하나의 요소를 포함할 컬렉션이 필요하지만 {1}개 요소가 발견되었습니다. {0} + + + + Expected exactly one item to match the predicate but found {1} item(s). {0} + 조건자에 일치하는 항목이 하나만 필요하지만 {1}개 항목이 발견되었습니다. {0} + + String '{0}' does contain string '{1}'. {2}. '{0}' 문자열에 '{1}' 문자열이 포함되어 있습니다. {2}. + + Expected collection to not contain the specified item. {0} + 지정한 항목을 포함하지 않을 컬렉션이 필요합니다. {0} + + + + Expected no items to match the predicate. {0} + 조건자와 일치하는 항목이 필요하지 않습니다. {0} + + + + String '{0}' ends with string '{1}'. {2} + 문자열 '{0}'은 문자열 '{1}'(으)로 끝납니다. {2} + + + + String '{0}' starts with string '{1}'. {2} + 문자열 '{0}'은 문자열 '{1}'(으)로 시작합니다. {2} + + + + Dynamic data field '{0}' should be static. + 동적 데이터 필드 '{0}'은(는) 정적이어야 합니다. + + - Dynamic data method '{0}' should be static, parameterless and non-generic. - 동적 데이터 메서드 '{0}'은(는) 정적이고 매개 변수가 없으며 제네릭이 아니어야 합니다. + Dynamic data method '{0}' should be static, non-generic, and cannot have 'params' parameter. + 동적 데이터 메서드 '{0}'은(는) 정적이고 제네릭이 아니어야 하며 'params' 매개 변수를 사용할 수 없습니다. @@ -98,8 +174,8 @@ - The dynamic data source '{0}' in type '{1}' should exist and be a property or a method. - '{1}' 형식의 동적 데이터 원본 '{0}' 존재하며 속성 또는 메서드여야 합니다. + The dynamic data source '{0}' in type '{1}' should exist and be a property, a method, or a field. + 형식 '{1}'의 동적 데이터 원본 '{0}'은(는) 속성, 메서드 또는 필드로 존재해야 합니다. @@ -122,9 +198,9 @@ Actual: {2} - String '{0}' does not end with string '{1}'. {2}. + String '{0}' does not end with string '{1}'. {2} '{0}' 문자열이 '{1}' 문자열로 끝나지 않습니다. {2} - + {0} failed. {1} @@ -141,15 +217,20 @@ Actual: {2} 잘못된 GitHub 티켓 URL + + Value '{0}' is not within the expected range [{1}, {2}]. {3} + '{0}' 값이 예상 범위 [{1}, {2}] 내에 있지 않습니다. {3} + + {0} Expected type:<{1}>. Actual type:<{2}>. {0} 예상 형식: <{1}>, 실제 형식: <{2}>. - String '{0}' does not match pattern '{1}'. {2}. + String '{0}' does not match pattern '{1}'. {2} '{0}' 문자열이 '{1}' 패턴과 일치하지 않습니다. {2} - + Expected collection to contain any item but it is empty. {0} @@ -162,14 +243,9 @@ Actual: {2} - String '{0}' matches pattern '{1}'. {2}. + String '{0}' matches pattern '{1}'. {2} '{0}' 문자열이 '{1}' 패턴과 일치합니다. {2} - - - - Assert.Equals should not be used for Assertions. Please use Assert.AreEqual & overloads instead. - 어설션에 Assert.Equals를 사용할 수 없습니다. 대신 Assert.AreEqual 및 오버로드를 사용하세요. - + @@ -182,7 +258,7 @@ Actual: {2} 또는 멤버가 기본 클래스에 정의된 프라이빗 멤버일 수 있습니다. 기본 클래스에 정의된 전용 멤버인 경우에는 이 멤버를 정의하는 형식을 PrivateObject의 생성자에 전달해야 합니다. - + @@ -195,7 +271,7 @@ Actual: {2} 또는 멤버가 기본 클래스에 정의된 프라이빗 멤버일 수 있습니다. 기본 클래스에 정의된 전용 멤버인 경우에는 이 멤버를 정의하는 형식을 PrivateObject의 생성자에 전달해야 합니다. - + The parameter '{0}' is invalid. The value cannot be null. {1}. @@ -207,10 +283,15 @@ Actual: {2} 요소 수가 다릅니다. + + Element(s) <{0}> is/are not present in the collection. + Element(s) <{0}> is/are not present in the collection. + + - String '{0}' does not start with string '{1}'. {2}. + String '{0}' does not start with string '{1}'. {2} '{0}' 문자열이 '{1}' 문자열로 시작되지 않습니다. {2} - + The property {0} has type {1}; expected type {2}. @@ -272,11 +353,6 @@ Actual: {2} {0}({1}) - - async TestMethod with UITestMethodAttribute are not supported. Either remove async or use TestMethodAttribute. - async TestMethod with UITestMethodAttribute는 지원되지 않습니다. async를 제거하거나 TestMethodAttribute를 사용하세요. - - Property or method {0} on {1} return type is not assignable to 'IEnumerable'. {1} 반환 형식의 속성 또는 메서드 {0}은(는) 'IEnumerable'에 할당할 수 없습니다. @@ -297,18 +373,65 @@ Actual: {2} {1}의 속성 또는 메서드 {0}이(가) 빈 IEnumerable<object[]>를 반환합니다. - - UITestMethodAttribute.DispatcherQueue should not be null. To use UITestMethodAttribute within a WinUI Desktop App, remember to set the static UITestMethodAttribute.DispatcherQueue during the test initialization. - UITestMethodAttribute.DispatcherQueue는 null이 아니어야 합니다. WinUI 데스크톱 앱 내에서 UITestMethodAttribute를 사용하려면 테스트 초기화 중에 정적 UITestMethodAttribute.DispatcherQueue를 설정해야 합니다. + + Actual value <{2}> is not greater than expected value <{1}>. {0} + 실제 값 <{2}>은(는)는 예상 값 <{1}>보다 크지 않습니다. {0} + + + + Actual value <{2}> is not greater than or equal to expected value <{1}>. {0} + 실제 값 <{2}>은(는)는 예상 값 <{1}>보다 크거나 같지 않습니다. {0} + + + + Actual value <{2}> is not less than expected value <{1}>. {0} + 실제 값 <{2}>은(는)는 예상 값 <{1}>보다 작지 않습니다. {0} + + + + Actual value <{2}> is not less than or equal to expected value <{1}>. {0} + 실제 값 <{2}>은(는)는 예상 값 <{1}>보다 작거나 같지 않습니다. {0} - - Type '{0}' is not assignable to '{1}'. - '{0}' 형식은 '{1}'에 할당할 수 없습니다. - - - {0} argument name like "applicationType" - - {1} fully qualified class name like "Microsoft.UI.Xaml.Application" - + + Expected value <{1}> to be positive. {0} + 예상 값 <{1}>은(는) 양수일 것으로 예상합니다. {0} + + + + Expected value <{1}> to be negative. {0} + 예상 값 <{1}>은(는) 음수일 것으로 예상합니다. {0} + + + + Assert.Equals should not be used for Assertions. Please use Assert.AreEqual & overloads instead. + 어설션에 Assert.Equals를 사용할 수 없습니다. 대신 Assert.AreEqual 및 오버로드를 사용하세요. + + + + Assert.ReferenceEquals should not be used for Assertions. Please use Assert.AreSame & overloads instead. + 어설션에 Assert.ReferenceEquals를 사용할 수 없습니다. 대신 Assert.AreSame 및 오버로드를 사용하세요. + + + + StringAssert.Equals should not be used for Assertions. Please use StringAssert methods or Assert.AreEqual & overloads instead. + 어설션에 StringAssert.Equals를 사용할 수 없습니다. 대신 StringAssert 메서드 또는 Assert.AreEqual 및 오버로드를 사용하세요. + + + + StringAssert.ReferenceEquals should not be used for Assertions. Please use StringAssert methods or Assert.AreSame & overloads instead. + StringAssert.ReferenceEquals는 Assertions에 사용할 수 없습니다. 대신 StringAssert 메서드 또는 Assert.AreSame 및 오버로드를 사용하세요. + + + + CollectionAssert.Equals should not be used for Assertions. Please use CollectionAssert.AreEqual & overloads instead. + 어설션에 CollectionAssert.Equals를 사용할 수 없습니다. 대신 CollectionAssert.AreEqual 및 오버로드를 사용하세요. + + + + CollectionAssert.ReferenceEquals should not be used for Assertions. Please use CollectionAssert methods or Assert.AreSame & overloads instead. + CollectionAssert.ReferenceEquals는 Assertions에 사용할 수 없습니다. 대신 CollectionAssert 메서드 또는 Assert.AreSame 및 오버로드를 사용하세요. + diff --git a/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.pl.xlf b/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.pl.xlf index 41afe70660..a35b7d0f19 100644 --- a/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.pl.xlf +++ b/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.pl.xlf @@ -37,6 +37,37 @@ Oczekiwana:<{1} ({2})>. Rzeczywista:<{3} ({4})>. {0} + + {0}{1} +{2} +{3} +{4} + {0}{1} +{2} +{3} +{4} + + + + Expected: + Oczekiwane: + + + + But was: + Ale było: + + + + String lengths are both {0} but differ at index {1}. + Długości ciągów są {0}, ale różnią się w indeksie {1}. + + + + Expected string length {0} but was {1}. + Oczekiwano ciągu o długości {0}, ale miał wartość {1}. + + Expected any value except:<{1}>. Actual:<{2}>. {0} Oczekiwano dowolnej wartości za wyjątkiem:<{1}>. Rzeczywista:<{2}>. {0} @@ -82,14 +113,59 @@ Ciąg „{0}” nie zawiera ciągu „{1}”. {2}. + + Expected collection to contain the specified item. {0} + Oczekiwano, że kolekcja będzie zawierać określony element. {0} + + + + Expected at least one item to match the predicate. {0} + Oczekiwano co najmniej jednego elementu zgodnego z predykatem. {0} + + + + Expected collection to contain exactly one element but found {1} element(s). {0} + Oczekiwano, że kolekcja będzie zawierać dokładnie jeden element, ale znaleziono {1} elementów. {0} + + + + Expected exactly one item to match the predicate but found {1} item(s). {0} + Oczekiwano dokładnie jednego elementu zgodnego z predykatem, ale znaleziono {1} elementów. {0} + + String '{0}' does contain string '{1}'. {2}. Ciąg „{0}” zawiera ciąg „{1}”. {2}. + + Expected collection to not contain the specified item. {0} + Oczekiwano, że kolekcja nie będzie zawierać określonego elementu. {0} + + + + Expected no items to match the predicate. {0} + Nie oczekiwano elementów zgodnych z predykatem. {0} + + + + String '{0}' ends with string '{1}'. {2} + Ciąg „{0}” kończy się ciągiem „{1}”. {2} + + + + String '{0}' starts with string '{1}'. {2} + Ciąg „{0}” rozpoczyna się od ciągu „{1}”. {2} + + + + Dynamic data field '{0}' should be static. + Dynamiczne pole danych „{0}” powinno być statyczne. + + - Dynamic data method '{0}' should be static, parameterless and non-generic. - Metoda danych dynamicznych „{0}” powinna być statyczna, bez parametrów i nie generyczna. + Dynamic data method '{0}' should be static, non-generic, and cannot have 'params' parameter. + Metoda danych dynamicznych „{0}” powinna być statyczna, niestandardowa i nie może mieć parametru „params”. @@ -98,8 +174,8 @@ - The dynamic data source '{0}' in type '{1}' should exist and be a property or a method. - Dynamiczne źródło danych '{0}' w typie '{1}' powinno istnieć i być właściwością lub metodą. + The dynamic data source '{0}' in type '{1}' should exist and be a property, a method, or a field. + Dynamiczne źródło danych „{0}” w typie „{1}” powinno istnieć i być właściwością, metodą lub polem. @@ -122,9 +198,9 @@ Rzeczywiste: {2} - String '{0}' does not end with string '{1}'. {2}. - Ciąg „{0}” nie kończy się ciągiem „{1}”. {2}. - + String '{0}' does not end with string '{1}'. {2} + Ciąg „{0}” nie kończy się ciągiem „{1}”. {2} + {0} failed. {1} @@ -141,15 +217,20 @@ Rzeczywiste: {2} Nieprawidłowy adres URL biletu usługi GitHub + + Value '{0}' is not within the expected range [{1}, {2}]. {3} + Wartość „{0}” nie mieści się w oczekiwanym zakresie [{1}, {2}]. {3} + + {0} Expected type:<{1}>. Actual type:<{2}>. {0} Oczekiwany typ:<{1}>. Rzeczywisty typ:<{2}>. - String '{0}' does not match pattern '{1}'. {2}. - Ciąg „{0}” nie jest zgodny ze wzorcem „{1}”. {2}. - + String '{0}' does not match pattern '{1}'. {2} + Ciąg „{0}” nie jest zgodny ze wzorcem „{1}”. {2} + Expected collection to contain any item but it is empty. {0} @@ -162,14 +243,9 @@ Rzeczywiste: {2} - String '{0}' matches pattern '{1}'. {2}. - Ciąg „{0}” jest zgodny ze wzorcem „{1}”. {2}. - - - - Assert.Equals should not be used for Assertions. Please use Assert.AreEqual & overloads instead. - Assert.Equals nie powinno być używane do potwierdzania. Zamiast tego użyj Assert.AreEqual i przeciążeń. - + String '{0}' matches pattern '{1}'. {2} + Ciąg „{0}” jest zgodny ze wzorcem „{1}”. {2} + @@ -182,7 +258,7 @@ Rzeczywiste: {2} lub składowa może być zdefiniowana jako prywatna w klasie bazowej. W drugim przypadku należy przekazać typ, który definiuje składową w konstruktorze obiektu PrivateObject. - + @@ -195,7 +271,7 @@ Rzeczywiste: {2} lub składowa może być zdefiniowana jako prywatna w klasie bazowej. W drugim przypadku należy przekazać typ, który definiuje składową w konstruktorze obiektu PrivateObject. - + The parameter '{0}' is invalid. The value cannot be null. {1}. @@ -207,10 +283,15 @@ Rzeczywiste: {2} Inna liczba elementów. + + Element(s) <{0}> is/are not present in the collection. + Element(s) <{0}> is/are not present in the collection. + + - String '{0}' does not start with string '{1}'. {2}. - Ciąg „{0}” nie rozpoczyna się od ciągu „{1}”. {2}. - + String '{0}' does not start with string '{1}'. {2} + Ciąg „{0}” nie rozpoczyna się od ciągu „{1}”. {2} + The property {0} has type {1}; expected type {2}. @@ -272,11 +353,6 @@ Rzeczywiste: {2} {0} ({1}) - - async TestMethod with UITestMethodAttribute are not supported. Either remove async or use TestMethodAttribute. - asynchroniczna metoda TestMethod z elementem UITestMethodAttribute nie są obsługiwane. Usuń element asynchroniczny lub użyj elementu TestMethodAttribute. - - Property or method {0} on {1} return type is not assignable to 'IEnumerable'. Właściwości lub metody {0} dla zwracanego typu {1} nie można przypisać do elementu „IEnumerable”. @@ -297,18 +373,65 @@ Rzeczywiste: {2} Właściwość lub metoda {0} w elemencie {1} zwraca pusty interfejs IEnumerable<object[]>. - - UITestMethodAttribute.DispatcherQueue should not be null. To use UITestMethodAttribute within a WinUI Desktop App, remember to set the static UITestMethodAttribute.DispatcherQueue during the test initialization. - Element UITestMethodAttribute.DispatcherQueue nie powinien mieć wartości null. Aby użyć atrybutu UITestMethodAttribute w aplikacji klasycznej WinUI, pamiętaj o ustawieniu statycznego atrybutu UITestMethodAttribute.DispatcherQueue podczas inicjowania testu. + + Actual value <{2}> is not greater than expected value <{1}>. {0} + Wartość rzeczywista <{2}> nie jest większa niż oczekiwana wartość <{1}>. {0} + + + + Actual value <{2}> is not greater than or equal to expected value <{1}>. {0} + Wartość rzeczywista <{2}> nie jest większa lub równa oczekiwanej wartości <{1}>. {0} + + + + Actual value <{2}> is not less than expected value <{1}>. {0} + Wartość rzeczywista <{2}> nie jest mniejsza niż oczekiwana wartość <{1}>. {0} + + + + Actual value <{2}> is not less than or equal to expected value <{1}>. {0} + Wartość rzeczywista <{2}> nie jest mniejsza lub równa oczekiwanej wartości <{1}>. {0} - - Type '{0}' is not assignable to '{1}'. - Typu „{0}” nie można przypisać do typu „{1}”. - - - {0} argument name like "applicationType" - - {1} fully qualified class name like "Microsoft.UI.Xaml.Application" - + + Expected value <{1}> to be positive. {0} + Oczekiwana wartość <{1}> powinna być dodatnia. {0} + + + + Expected value <{1}> to be negative. {0} + Oczekiwana wartość <{1}> powinna być negatywna. {0} + + + + Assert.Equals should not be used for Assertions. Please use Assert.AreEqual & overloads instead. + Assert.Equals nie powinno być używane do potwierdzania. Zamiast tego użyj Assert.AreEqual i przeciążeń. + + + + Assert.ReferenceEquals should not be used for Assertions. Please use Assert.AreSame & overloads instead. + Element Assert.ReferenceEquals nie powinien być używany dla asercji. Zamiast tego użyj metody Assert.AreSame i przeciążeń. + + + + StringAssert.Equals should not be used for Assertions. Please use StringAssert methods or Assert.AreEqual & overloads instead. + Element StringAssert.Equals nie powinien być używany dla asercji. Zamiast tego użyj metod StringAssert lub Assert.AreEqual i przeciążeń. + + + + StringAssert.ReferenceEquals should not be used for Assertions. Please use StringAssert methods or Assert.AreSame & overloads instead. + Element StringAssert.ReferenceEquals nie powinien być używany dla asercji. Zamiast tego użyj metod StringAssert lub Assert.AreSame i obciążeń. + + + + CollectionAssert.Equals should not be used for Assertions. Please use CollectionAssert.AreEqual & overloads instead. + Element CollectionAssert.Equals nie powinien być używany dla asercji. Zamiast tego użyj metody CollectionAssert.AreEqual i przeciążeń. + + + + CollectionAssert.ReferenceEquals should not be used for Assertions. Please use CollectionAssert methods or Assert.AreSame & overloads instead. + Element Assert.ReferenceEquals nie powinien być używany dla asercji. Zamiast tego użyj metod CollectionAssert lub Assert.AreSame oraz ich przeciążeń. + diff --git a/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.pt-BR.xlf b/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.pt-BR.xlf index 8078eeba69..85960e12b8 100644 --- a/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.pt-BR.xlf +++ b/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.pt-BR.xlf @@ -37,6 +37,37 @@ Esperado:<{1} ({2})>. Real:<{3} ({4})>. {0} + + {0}{1} +{2} +{3} +{4} + {0}{1} +{2} +{3} +{4} + + + + Expected: + Esperado: + + + + But was: + Mas foi: + + + + String lengths are both {0} but differ at index {1}. + Os comprimentos de cadeia de caracteres são ambos {0}, mas diferem no índice {1}. + + + + Expected string length {0} but was {1}. + Comprimento esperado da cadeia de caracteres {0}, mas foi {1}. + + Expected any value except:<{1}>. Actual:<{2}>. {0} Esperado qualquer valor exceto:<{1}>. Real:<{2}>. {0} @@ -82,14 +113,59 @@ A cadeia de caracteres '{0}' não contém a cadeia de caracteres '{1}'. {2}. + + Expected collection to contain the specified item. {0} + A coleção esperada contém o item especificado. {0} + + + + Expected at least one item to match the predicate. {0} + Esperava-se que pelo menos um item correspondesse ao predicado. {0} + + + + Expected collection to contain exactly one element but found {1} element(s). {0} + A coleção esperada contém exatamente um elemento, mas encontrou {1} elementos. {0} + + + + Expected exactly one item to match the predicate but found {1} item(s). {0} + Esperava-se exatamente um item para corresponder ao predicado, mas encontrou {1} itens. {0} + + String '{0}' does contain string '{1}'. {2}. A cadeia de caracteres "{0}" contém a cadeia de caracteres "{1}". {2}. + + Expected collection to not contain the specified item. {0} + A coleção esperada não contém o item especificado. {0} + + + + Expected no items to match the predicate. {0} + Não era esperado nenhum item que corresponda ao predicado. {0} + + + + String '{0}' ends with string '{1}'. {2} + A cadeia de caracteres “{0}” termina com cadeia de caracteres “{1}”. {2} + + + + String '{0}' starts with string '{1}'. {2} + A cadeia de caracteres “{0}” começa com a cadeia de caracteres “{1}”. {2} + + + + Dynamic data field '{0}' should be static. + O campo de dados dinâmicos '{0}' deve ser estático. + + - Dynamic data method '{0}' should be static, parameterless and non-generic. - O método de dados dinâmicos "{0}" deve ser estático, sem parâmetros e não genérico. + Dynamic data method '{0}' should be static, non-generic, and cannot have 'params' parameter. + O método de dados dinâmicos '{0}' deve ser estático, não genérico e não pode ter um parâmetro 'params'. @@ -98,8 +174,8 @@ - The dynamic data source '{0}' in type '{1}' should exist and be a property or a method. - A fonte de dados dinâmica '{0}' no tipo '{1}' deve existir e ser uma propriedade ou um método. + The dynamic data source '{0}' in type '{1}' should exist and be a property, a method, or a field. + A fonte de dados dinâmica '{0}' do tipo '{1}' deve existir e ser uma propriedade, um método ou um campo. @@ -122,9 +198,9 @@ Real: {2} - String '{0}' does not end with string '{1}'. {2}. - A cadeia de caracteres '{0}' não termina com a cadeia de caracteres '{1}'. {2}. - + String '{0}' does not end with string '{1}'. {2} + A cadeia de caracteres “{0}” não termina com a cadeia de caracteres “{1}”. {2} + {0} failed. {1} @@ -141,15 +217,20 @@ Real: {2} URL de tíquete do GitHub inválida + + Value '{0}' is not within the expected range [{1}, {2}]. {3} + O valor '{0}' não está dentro do intervalo esperado [{1}, {2}]. {3} + + {0} Expected type:<{1}>. Actual type:<{2}>. {0} Tipo esperado:<{1}>. Tipo real:<{2}>. - String '{0}' does not match pattern '{1}'. {2}. - A cadeia de caracteres '{0}' não corresponde ao padrão '{1}'. {2}. - + String '{0}' does not match pattern '{1}'. {2} + A cadeia de caracteres “{0}” não corresponde ao padrão “{1}”. {2} + Expected collection to contain any item but it is empty. {0} @@ -162,14 +243,9 @@ Real: {2} - String '{0}' matches pattern '{1}'. {2}. - A cadeia de caracteres '{0}' corresponde ao padrão '{1}'. {2}. - - - - Assert.Equals should not be used for Assertions. Please use Assert.AreEqual & overloads instead. - Assert.Equals não deveria ser usado para Declarações. Use Assert.AreEqual e sobrecargas em seu lugar. - + String '{0}' matches pattern '{1}'. {2} + A cadeia de caracteres “{0}” corresponde ao padrão “{1}”. {2} + @@ -182,7 +258,7 @@ Real: {2} ou o membro pode ser particular e definido em uma classe base. Se o último caso for verdadeiro, será necessário passar o tipo que define o membro no construtor do PrivateObject. - + @@ -195,7 +271,7 @@ Real: {2} ou o membro pode ser particular e definido em uma classe base. Se o último caso for verdadeiro, será necessário passar o tipo que define o membro para o construtor do PrivateObject. - + The parameter '{0}' is invalid. The value cannot be null. {1}. @@ -207,10 +283,15 @@ Real: {2} Número diferente de elementos. + + Element(s) <{0}> is/are not present in the collection. + Element(s) <{0}> is/are not present in the collection. + + - String '{0}' does not start with string '{1}'. {2}. - A cadeia de caracteres '{0}' não começa com a cadeia de caracteres '{1}'. {2}. - + String '{0}' does not start with string '{1}'. {2} + A cadeia de caracteres “{0}” não começa com a cadeia de caracteres “{1}”. {2} + The property {0} has type {1}; expected type {2}. @@ -272,11 +353,6 @@ Real: {2} {0} ({1}) - - async TestMethod with UITestMethodAttribute are not supported. Either remove async or use TestMethodAttribute. - TestMethod assíncrono com UITestMethodAttribute não têm suporte. Remova o assíncrono ou use o TestMethodAttribute. - - Property or method {0} on {1} return type is not assignable to 'IEnumerable'. A propriedade ou o método {0} no tipo de retorno {1} não podem ser atribuídos a "IEnumerable". @@ -297,18 +373,65 @@ Real: {2} A propriedade ou o método {0} em {1} retorna um IEnumerable<object[]> vazio. - - UITestMethodAttribute.DispatcherQueue should not be null. To use UITestMethodAttribute within a WinUI Desktop App, remember to set the static UITestMethodAttribute.DispatcherQueue during the test initialization. - UITestMethodAttribute.DispatcherQueue não deve ser nulo. Para usar UITestMethodAttribute em um aplicativo WinUI Desktop, lembre-se de definir o UITestMethodAttribute.DispatcherQueue estático durante a inicialização do teste. + + Actual value <{2}> is not greater than expected value <{1}>. {0} + O valor <{2}> real não é maior que o valor esperado <{1}>. {0} + + + + Actual value <{2}> is not greater than or equal to expected value <{1}>. {0} + O valor <{2}> real não é maior ou igual ao valor esperado <{1}>. {0} + + + + Actual value <{2}> is not less than expected value <{1}>. {0} + O valor <{2}> real não é menor que o valor esperado <{1}>. {0} + + + + Actual value <{2}> is not less than or equal to expected value <{1}>. {0} + O valor <{2}> real não é menor ou igual ao valor esperado <{1}>. {0} - - Type '{0}' is not assignable to '{1}'. - Tipo '{0}' não é atribuível a '{1}'. - - - {0} argument name like "applicationType" - - {1} fully qualified class name like "Microsoft.UI.Xaml.Application" - + + Expected value <{1}> to be positive. {0} + O valor <{1}> esperado deve ser positivo. {0} + + + + Expected value <{1}> to be negative. {0} + O valor <{1}> esperado deve ser negativo. {0} + + + + Assert.Equals should not be used for Assertions. Please use Assert.AreEqual & overloads instead. + Assert.Equals não deveria ser usado para Declarações. Use Assert.AreEqual e sobrecargas em seu lugar. + + + + Assert.ReferenceEquals should not be used for Assertions. Please use Assert.AreSame & overloads instead. + Assert.ReferenceEquals não deve ser usado com as Declarações. Em vez disso, use Assert.AreSame e as sobrecargas. + + + + StringAssert.Equals should not be used for Assertions. Please use StringAssert methods or Assert.AreEqual & overloads instead. + StringAssert.Equals não deve ser usado com as Declarações. Em vez disso, use os métodos StringAssert ou Assert.AreEqual e as sobrecargas. + + + + StringAssert.ReferenceEquals should not be used for Assertions. Please use StringAssert methods or Assert.AreSame & overloads instead. + StringAssert.ReferenceEquals não deve ser usado com as Declarações. Em vez disso, use os métodos StringAssert ou Assert.AreSame e as sobrecargas. + + + + CollectionAssert.Equals should not be used for Assertions. Please use CollectionAssert.AreEqual & overloads instead. + CollectionAssert.Equals não deve ser usado com as Declarações. Em vez disso, use CollectionAssert.AreEqual e as sobrecargas. + + + + CollectionAssert.ReferenceEquals should not be used for Assertions. Please use CollectionAssert methods or Assert.AreSame & overloads instead. + CollectionAssert.ReferenceEquals não deve ser usado com as Declarações. Em vez disso, use os métodos CollectionAssert ou Assert.AreSame e as sobrecargas. + diff --git a/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.ru.xlf b/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.ru.xlf index f27e54ae56..76d4ab548d 100644 --- a/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.ru.xlf +++ b/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.ru.xlf @@ -37,6 +37,37 @@ Ожидается: <{1} ({2})>. Фактически: <{3} ({4})>. {0} + + {0}{1} +{2} +{3} +{4} + {0}{1} +{2} +{3} +{4} + + + + Expected: + Ожидалось: + + + + But was: + Было: + + + + String lengths are both {0} but differ at index {1}. + Длины строк равны {0}, но различаются по индексу {1}. + + + + Expected string length {0} but was {1}. + Ожидалась длина строки: {0}, фактическая длина строки: {1}. + + Expected any value except:<{1}>. Actual:<{2}>. {0} Ожидается любое значение, кроме: <{1}>. Фактически: <{2}>. {0} @@ -82,14 +113,59 @@ Строка "{0}" не содержит строку "{1}". {2}. + + Expected collection to contain the specified item. {0} + Ожидалось, что коллекция будет содержать указанный элемент. {0} + + + + Expected at least one item to match the predicate. {0} + Ожидался по крайней мере один элемент, соответствующий предикату. {0} + + + + Expected collection to contain exactly one element but found {1} element(s). {0} + Ожидалось, что коллекция будет содержать ровно один элемент, но найдено элементов: {1}. {0} + + + + Expected exactly one item to match the predicate but found {1} item(s). {0} + Ожидался ровно один элемент, соответствующий предикату, но найдено элементов: {1}. {0} + + String '{0}' does contain string '{1}'. {2}. Строка "{0}" не содержит строку "{1}". {2}. + + Expected collection to not contain the specified item. {0} + Ожидалось, что коллекция не будет содержать указанный элемент. {0} + + + + Expected no items to match the predicate. {0} + Ожидалось, что ни один элемент не будет соответствовать предикату. {0} + + + + String '{0}' ends with string '{1}'. {2} + Строка "{0}" заканчивается строкой "{1}". {2} + + + + String '{0}' starts with string '{1}'. {2} + Строка "{0}" начинается со строки "{1}". {2} + + + + Dynamic data field '{0}' should be static. + Поле динамических данных "{0}" должно быть статическим. + + - Dynamic data method '{0}' should be static, parameterless and non-generic. - Метод динамических данных "{0}" должен быть статическим, не иметь параметров и быть неуниверсальным. + Dynamic data method '{0}' should be static, non-generic, and cannot have 'params' parameter. + Метод динамических данных "{0}" должен быть статическим, неуниверсальным и не может иметь содержать параметр params. @@ -98,8 +174,8 @@ - The dynamic data source '{0}' in type '{1}' should exist and be a property or a method. - Динамический источник '{0}' в типе '{1}' должен существовать и быть свойством или методом. + The dynamic data source '{0}' in type '{1}' should exist and be a property, a method, or a field. + Динамический источник данных "{0}" в типе "{1}" должен существовать и быть свойством, методом или полем. @@ -122,9 +198,9 @@ Actual: {2} - String '{0}' does not end with string '{1}'. {2}. - Строка "{0}" не оканчивается строкой "{1}". {2}. - + String '{0}' does not end with string '{1}'. {2} + Строка "{0}" не заканчивается строкой "{1}". {2} + {0} failed. {1} @@ -141,15 +217,20 @@ Actual: {2} Недопустимый URL-адрес билета GitHub + + Value '{0}' is not within the expected range [{1}, {2}]. {3} + Значение "{0}" не находится в пределах ожидаемого диапазона [{1}, {2}]. {3} + + {0} Expected type:<{1}>. Actual type:<{2}>. {0}Ожидается тип: <{1}>. Фактический тип: <{2}>. - String '{0}' does not match pattern '{1}'. {2}. - Строка "{0}" не соответствует шаблону "{1}". {2}. - + String '{0}' does not match pattern '{1}'. {2} + Строка "{0}" не соответствует шаблону "{1}". {2} + Expected collection to contain any item but it is empty. {0} @@ -162,14 +243,9 @@ Actual: {2} - String '{0}' matches pattern '{1}'. {2}. - Строка "{0}" соответствует шаблону "{1}". {2}. - - - - Assert.Equals should not be used for Assertions. Please use Assert.AreEqual & overloads instead. - Нельзя использовать Assert.Equals для Assertions. Вместо этого используйте Assert.AreEqual и перегрузки. - + String '{0}' matches pattern '{1}'. {2} + Строка "{0}" соответствует шаблону "{1}". {2} + @@ -182,7 +258,7 @@ Actual: {2} либо член является закрытым и определенным на основе базового класса. Если справедливо последнее, то необходимо передать тип, определяющий член, в конструктор для PrivateObject. - + @@ -195,7 +271,7 @@ Actual: {2} метод доступа либо член является закрытым и определенным на основе базового класса. Если справедливо последнее, то необходимо передать тип, определяющий член, в конструктор для PrivateObject. - + The parameter '{0}' is invalid. The value cannot be null. {1}. @@ -207,10 +283,15 @@ Actual: {2} Разное число элементов. + + Element(s) <{0}> is/are not present in the collection. + Element(s) <{0}> is/are not present in the collection. + + - String '{0}' does not start with string '{1}'. {2}. - Строка "{0}" не начинается со строки "{1}". {2}. - + String '{0}' does not start with string '{1}'. {2} + Строка "{0}" не начинается со строки "{1}". {2} + The property {0} has type {1}; expected type {2}. @@ -272,11 +353,6 @@ Actual: {2} {0} ({1}) - - async TestMethod with UITestMethodAttribute are not supported. Either remove async or use TestMethodAttribute. - Асинхронный метод TestMethod с UITestMethodAttribute не поддерживается. Удалите префикс async или используйте TestMethodAttribute. - - Property or method {0} on {1} return type is not assignable to 'IEnumerable'. Свойство или метод {0} в {1} тип возвращаемого значения не может быть назначен "IEnumerable". @@ -297,18 +373,65 @@ Actual: {2} Свойство или метод {0} класса {1} возвращает пустой IEnumerable<object[]>. - - UITestMethodAttribute.DispatcherQueue should not be null. To use UITestMethodAttribute within a WinUI Desktop App, remember to set the static UITestMethodAttribute.DispatcherQueue during the test initialization. - Параметр UITestMethodAttribute.DispatcherQueue не должен иметь значение NULL. Чтобы использовать параметр UITestMethodAttribute в классических приложениях WinUI, не забудьте задать статический параметр UITestMethodAttribute.DispatcherQueue во время инициализации теста. + + Actual value <{2}> is not greater than expected value <{1}>. {0} + Действительное значение <{2}> не больше ожидаемого значения <{1}>. {0} + + + + Actual value <{2}> is not greater than or equal to expected value <{1}>. {0} + Действительное значение <{2}> не больше или не равно ожидаемому значению <{1}>. {0} + + + + Actual value <{2}> is not less than expected value <{1}>. {0} + Действительное значение <{2}> не меньше ожидаемого значения <{1}>. {0} + + + + Actual value <{2}> is not less than or equal to expected value <{1}>. {0} + Действительное значение <{2}> не меньше или не равно ожидаемому значению <{1}>. {0} - - Type '{0}' is not assignable to '{1}'. - Тип "{0}" не может быть назначен "{1}". - - - {0} argument name like "applicationType" - - {1} fully qualified class name like "Microsoft.UI.Xaml.Application" - + + Expected value <{1}> to be positive. {0} + Ожидалось, что значение <{1}> будет положительным. {0} + + + + Expected value <{1}> to be negative. {0} + Ожидалось, что значение <{1}> будет отрицательным. {0} + + + + Assert.Equals should not be used for Assertions. Please use Assert.AreEqual & overloads instead. + Нельзя использовать Assert.Equals для Assertions. Вместо этого используйте Assert.AreEqual и перегрузки. + + + + Assert.ReferenceEquals should not be used for Assertions. Please use Assert.AreSame & overloads instead. + Нельзя использовать Assert.ReferenceEquals для Assertions. Вместо этого используйте Assert.AreSame и перегрузки. + + + + StringAssert.Equals should not be used for Assertions. Please use StringAssert methods or Assert.AreEqual & overloads instead. + Нельзя использовать StringAssert.Equals для Assertions. Вместо этого используйте методы StringAssert или Assert.AreEqual и перегрузки. + + + + StringAssert.ReferenceEquals should not be used for Assertions. Please use StringAssert methods or Assert.AreSame & overloads instead. + Нельзя использовать StringAssert.ReferenceEquals для Assertions. Вместо этого используйте методы StringAssert или Assert.AreSame и перегрузки. + + + + CollectionAssert.Equals should not be used for Assertions. Please use CollectionAssert.AreEqual & overloads instead. + Нельзя использовать CollectionAssert.Equals для Assertions. Вместо этого используйте CollectionAssert.AreEqual и перегрузки. + + + + CollectionAssert.ReferenceEquals should not be used for Assertions. Please use CollectionAssert methods or Assert.AreSame & overloads instead. + Нельзя использовать CollectionAssert.ReferenceEquals для Assertions. Вместо этого используйте методы CollectionAssert или Assert.AreSame и перегрузки. + diff --git a/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.tr.xlf b/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.tr.xlf index 6ba4ddec3d..1f34a6bc07 100644 --- a/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.tr.xlf +++ b/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.tr.xlf @@ -37,6 +37,37 @@ Beklenen:<{1} ({2})>. Gerçek:<{3} ({4})>. {0} + + {0}{1} +{2} +{3} +{4} + {0}{1} +{2} +{3} +{4} + + + + Expected: + Beklenen: + + + + But was: + Ancak: + + + + String lengths are both {0} but differ at index {1}. + Dize uzunlukları her ikisi de {0} ama dizin {1} üzerinde farklılık gösterir. + + + + Expected string length {0} but was {1}. + Beklenen dize uzunluğu {0} idi, ancak dize uzunluğu {1} oldu. + + Expected any value except:<{1}>. Actual:<{2}>. {0} Şunun dışında bir değer bekleniyor:<{1}>. Gerçek:<{2}>. {0} @@ -82,14 +113,59 @@ '{0}' dizesi, '{1}' dizesini içermiyor. {2}. + + Expected collection to contain the specified item. {0} + Koleksiyonun belirtilen öğeyi içermesi bekleniyordu. {0} + + + + Expected at least one item to match the predicate. {0} + En az bir öğenin koşulla eşleşmesi bekleniyordu. {0} + + + + Expected collection to contain exactly one element but found {1} element(s). {0} + Beklenen koleksiyonun tam olarak bir öğe içermesi beklenirken, {1} öğe bulundu. {0} + + + + Expected exactly one item to match the predicate but found {1} item(s). {0} + Özellikle bir öğenin koşulla eşleşmesi beklenirken {1} öğe bulundu. {0} + + String '{0}' does contain string '{1}'. {2}. '{0}' dizesi, '{1}' dizesini içermiyor. {2}. + + Expected collection to not contain the specified item. {0} + Koleksiyonun belirtilen öğeyi içermemesi bekleniyordu. {0} + + + + Expected no items to match the predicate. {0} + Hiçbir öğenin koşulla eşleşmesi beklenmiyordu. {0} + + + + String '{0}' ends with string '{1}'. {2} + '{0}' dizesi '{1}' dizesi ile bitiyor. {2} + + + + String '{0}' starts with string '{1}'. {2} + '{0}' dizesi '{1}' dizesi ile başlıyor. {2} + + + + Dynamic data field '{0}' should be static. + Dinamik veri alanı ‘{0}’ statik olmalıdır. + + - Dynamic data method '{0}' should be static, parameterless and non-generic. - '{0}' dinamik veri yöntemi statik ve parametresiz olmalı ve genel olmamalıdır. + Dynamic data method '{0}' should be static, non-generic, and cannot have 'params' parameter. + Dinamik veri yöntemi ‘{0}’ statik, genel olmayan ve ‘params’ parametresine sahip olamaz. @@ -98,8 +174,8 @@ - The dynamic data source '{0}' in type '{1}' should exist and be a property or a method. - Dinamik veri kaynağı '{0}' türdeki '{1}' bir özellik veya yöntem olmalıdır. + The dynamic data source '{0}' in type '{1}' should exist and be a property, a method, or a field. + ‘{1}’ türündeki dinamik veri kaynağı ‘{0}’ mevcut olmalı ve bir özellik, yöntem veya alan olmalıdır. @@ -122,9 +198,9 @@ Gerçekte olan: {2} - String '{0}' does not end with string '{1}'. {2}. - '{0}' dizesi, '{1}' dizesi ile sonlanmıyor. {2}. - + String '{0}' does not end with string '{1}'. {2} + '{0}' dizesi, '{1}' dizesi ile sonlanmıyor. {2} + {0} failed. {1} @@ -141,15 +217,20 @@ Gerçekte olan: {2} Geçersiz GitHub anahtar URL'si + + Value '{0}' is not within the expected range [{1}, {2}]. {3} + '{0}' değeri beklenen aralıkta [{1}, {2}] değil. {3} + + {0} Expected type:<{1}>. Actual type:<{2}>. {0} Beklenen tür:<{1}>. Gerçek tür:<{2}>. - String '{0}' does not match pattern '{1}'. {2}. - '{0}' dizesi, '{1}' deseni ile eşleşmiyor. {2}. - + String '{0}' does not match pattern '{1}'. {2} + '{0}' dizesi, '{1}' deseni ile eşleşmiyor. {2} + Expected collection to contain any item but it is empty. {0} @@ -162,14 +243,9 @@ Gerçekte olan: {2} - String '{0}' matches pattern '{1}'. {2}. - '{0}' dizesi, '{1}' deseni ile eşleşiyor. {2}. - - - - Assert.Equals should not be used for Assertions. Please use Assert.AreEqual & overloads instead. - Assert.Equals, Onaylama için kullanılmamalı. Lütfen yerine Assert.AreEqual & aşırı yüklemeleri kullanın. - + String '{0}' matches pattern '{1}'. {2} + '{0}' dizesi, '{1}' deseni ile eşleşiyor. {2} + @@ -182,7 +258,7 @@ Gerçekte olan: {2} veya üye, özel olabilir ve temel sınıfta tanımlı olabilir. Eğer ikincisi doğru ise, türü geçirmeniz gerekir; bu tür PrivateObject'in oluşturucusunda üyeyi tanımlar. - + @@ -195,7 +271,7 @@ Gerçekte olan: {2} veya üye, özel olabilir ve temel sınıfta tanımlı olabilir. Eğer ikincisi doğru ise, türü geçirmeniz gerekir; bu tür, PrivateObject'in oluşturucusunda üyeyi tanımlar. - + The parameter '{0}' is invalid. The value cannot be null. {1}. @@ -207,10 +283,15 @@ Gerçekte olan: {2} Öğe sayıları farklı. + + Element(s) <{0}> is/are not present in the collection. + Element(s) <{0}> is/are not present in the collection. + + - String '{0}' does not start with string '{1}'. {2}. - '{0}' dizesi, '{1}' dizesi ile başlamıyor. {2}. - + String '{0}' does not start with string '{1}'. {2} + '{0}' dizesi, '{1}' dizesi ile başlamıyor. {2} + The property {0} has type {1}; expected type {2}. @@ -272,11 +353,6 @@ Gerçekte olan: {2} {0} ({1}) - - async TestMethod with UITestMethodAttribute are not supported. Either remove async or use TestMethodAttribute. - UITestMethodAttribute özniteliğine sahip async TestMethod metodu desteklenmiyor. async ifadesini kaldırın ya da TestMethodAttribute özniteliğini kullanın. - - Property or method {0} on {1} return type is not assignable to 'IEnumerable'. {1} dönüş türündeki {0} özelliği veya yöntemi 'IEnumerable' öğesine atanamaz. @@ -297,18 +373,65 @@ Gerçekte olan: {2} {1} üzerindeki {0} özelliği veya metodu boş IEnumerable<object[]> döndürür. - - UITestMethodAttribute.DispatcherQueue should not be null. To use UITestMethodAttribute within a WinUI Desktop App, remember to set the static UITestMethodAttribute.DispatcherQueue during the test initialization. - UITestMethodAttribute.DispatcherQueue boş olmamalıdır. UITestMethodAttribute'ı bir WinUI Masaüstü Uygulamasında kullanmak için, test başlatma sırasında statik UITestMethodAttribute.DispatcherQueue'yu ayarlamayı unutmayın. + + Actual value <{2}> is not greater than expected value <{1}>. {0} + Geçerli <{2}> değeri, beklenen <{1}> değerinden daha büyük değil. {0} + + + + Actual value <{2}> is not greater than or equal to expected value <{1}>. {0} + Geçerli <{2}> değeri, beklenen <{1}> değerinden büyük veya bu değere eşit değil. {0} + + + + Actual value <{2}> is not less than expected value <{1}>. {0} + Geçerli <{2}> değeri, beklenen <{1}> değerinden daha küçük değil. {0} + + + + Actual value <{2}> is not less than or equal to expected value <{1}>. {0} + Geçerli <{2}> değeri, beklenen <{1}> değerinden küçük veya bu değere eşit değil. {0} - - Type '{0}' is not assignable to '{1}'. - '{0}' tipi '{1}'ye atanamaz. - - - {0} argument name like "applicationType" - - {1} fully qualified class name like "Microsoft.UI.Xaml.Application" - + + Expected value <{1}> to be positive. {0} + Beklenen <{1}> değeri pozitif olmalıdır. {0} + + + + Expected value <{1}> to be negative. {0} + Beklenen <{1}> değeri negatif olmalıdır. {0} + + + + Assert.Equals should not be used for Assertions. Please use Assert.AreEqual & overloads instead. + Assert.Equals, Onaylama için kullanılmamalı. Lütfen yerine Assert.AreEqual & aşırı yüklemeleri kullanın. + + + + Assert.ReferenceEquals should not be used for Assertions. Please use Assert.AreSame & overloads instead. + Assert.ReferenceEquals, Onaylama için kullanılmamalı. Lütfen yerine Assert.AreSame & aşırı yüklemeleri kullanın. + + + + StringAssert.Equals should not be used for Assertions. Please use StringAssert methods or Assert.AreEqual & overloads instead. + StringAssert.Equals, Onaylama için kullanılmamalı. Lütfen bunun yerine StringAssert yöntemlerini veya Assert.AreEqual & aşırı yüklemelerini kullanın. + + + + StringAssert.ReferenceEquals should not be used for Assertions. Please use StringAssert methods or Assert.AreSame & overloads instead. + StringAssert.ReferenceEquals, Onaylama için kullanılmamalı. Lütfen bunun yerine StringAssert yöntemlerini veya Assert.AreSame & aşırı yüklemelerini kullanın. + + + + CollectionAssert.Equals should not be used for Assertions. Please use CollectionAssert.AreEqual & overloads instead. + CollectionAssert.Equals, Onaylama için kullanılmamalı. Lütfen yerine CollectionAssert.AreEqual & aşırı yüklemeleri kullanın. + + + + CollectionAssert.ReferenceEquals should not be used for Assertions. Please use CollectionAssert methods or Assert.AreSame & overloads instead. + CollectionAssert.ReferenceEquals, Onaylama için kullanılmamalı. Lütfen bunun yerine CollectionAssert yöntemlerini veya Assert.AreSame & aşırı yüklemelerini kullanın. + diff --git a/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.zh-Hans.xlf b/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.zh-Hans.xlf index f8f3b68731..6bc1ce182d 100644 --- a/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.zh-Hans.xlf +++ b/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.zh-Hans.xlf @@ -37,6 +37,37 @@ 应为: <{1} ({2})>,实际为: <{3} ({4})>。{0} + + {0}{1} +{2} +{3} +{4} + {0}{1} +{2} +{3} +{4} + + + + Expected: + 应为: + + + + But was: + 但却是: + + + + String lengths are both {0} but differ at index {1}. + 字符串长度均为 {0},但在索引 {1} 处有所不同。 + + + + Expected string length {0} but was {1}. + 字符串长度应为 {0},但为 {1}。 + + Expected any value except:<{1}>. Actual:<{2}>. {0} 应为: <{1}> 以外的任意值,实际为: <{2}>。{0} @@ -82,14 +113,59 @@ 字符串“{0}”不包含字符串“{1}”。{2}。 + + Expected collection to contain the specified item. {0} + 预期集合包含指定项。{0} + + + + Expected at least one item to match the predicate. {0} + 应至少有一项与谓词匹配。{0} + + + + Expected collection to contain exactly one element but found {1} element(s). {0} + 预期集合仅包含一个元素,但找到 {1} 个元素。{0} + + + + Expected exactly one item to match the predicate but found {1} item(s). {0} + 应恰好只有一项与谓词匹配,但找到 {1} 项。{0} + + String '{0}' does contain string '{1}'. {2}. 字符串“{0}”确实包含字符串“{1}”。{2} + + Expected collection to not contain the specified item. {0} + 预期集合不包含指定项。{0} + + + + Expected no items to match the predicate. {0} + 预期没有与谓词匹配的项。{0} + + + + String '{0}' ends with string '{1}'. {2} + 字符串 '{0}' 以字符串 '{1}'结尾。{2} + + + + String '{0}' starts with string '{1}'. {2} + 字符串 '{0}' 以字符串 '{1}' 开头。{2} + + + + Dynamic data field '{0}' should be static. + 动态数据字段“{0}”应该是静态的。 + + - Dynamic data method '{0}' should be static, parameterless and non-generic. - 动态数据方法“{0}”应为静态、无参数和非泛型。 + Dynamic data method '{0}' should be static, non-generic, and cannot have 'params' parameter. + 动态数据方法 ‘{0}’ 应为静态、非泛型,并且不能具有 ‘params’ 参数。 @@ -98,8 +174,8 @@ - The dynamic data source '{0}' in type '{1}' should exist and be a property or a method. - 类型 '{1}' 中的动态数据源 '{0}' 应存在,并且应为属性或方法。 + The dynamic data source '{0}' in type '{1}' should exist and be a property, a method, or a field. + 类型“{1}”中的动态数据源“{0}”应该存在,并且应为属性、方法或字段。 @@ -122,9 +198,9 @@ Actual: {2} - String '{0}' does not end with string '{1}'. {2}. - 字符串“{0}”不以字符串“{1}”结尾。{2}。 - + String '{0}' does not end with string '{1}'. {2} + 字符串 '{0}' 不以字符串 '{1}' 结尾。{2} + {0} failed. {1} @@ -141,15 +217,20 @@ Actual: {2} GitHub 票证 URL 无效 + + Value '{0}' is not within the expected range [{1}, {2}]. {3} + 值“{0}”不在预期范围 [{1}, {2}] 内。{3} + + {0} Expected type:<{1}>. Actual type:<{2}>. {0} 类型应为: <{1}>。类型实为: <{2}>。 - String '{0}' does not match pattern '{1}'. {2}. - 字符串“{0}”与模式“{1}”不匹配。{2}。 - + String '{0}' does not match pattern '{1}'. {2} + 字符串 '{0}' 与模式 '{1}' 不匹配。{2} + Expected collection to contain any item but it is empty. {0} @@ -162,14 +243,9 @@ Actual: {2} - String '{0}' matches pattern '{1}'. {2}. - 字符串“{0}”与模式“{1}”匹配。{2}。 - - - - Assert.Equals should not be used for Assertions. Please use Assert.AreEqual & overloads instead. - Assert.Equals 不应用于断言。请改用 Assert.AreEqual 和重载。 - + String '{0}' matches pattern '{1}'. {2} + 字符串 '{0}' 与模式 '{1}' 匹配。{2} + @@ -182,7 +258,7 @@ Actual: {2} 或者该成员可能是私有成员并且是在基类上定义的。如果属于后一种情况,则需要将定义 该成员的类型传递到 PrivateObject 的构造函数中。 - + @@ -195,7 +271,7 @@ Actual: {2} 或者该成员可能是私有成员并且是在基类上定义的。如果属于后一种情况,则需要将定义 该成员的类型传递到 PrivateObject 的构造函数。 - + The parameter '{0}' is invalid. The value cannot be null. {1}. @@ -207,10 +283,15 @@ Actual: {2} 不同数量的元素。 + + Element(s) <{0}> is/are not present in the collection. + Element(s) <{0}> is/are not present in the collection. + + - String '{0}' does not start with string '{1}'. {2}. - 字符串“{0}”没有以字符串“{1}”开头。{2}。 - + String '{0}' does not start with string '{1}'. {2} + 字符串 '{0}' 不以字符串 '{1}' 开头。{2} + The property {0} has type {1}; expected type {2}. @@ -272,11 +353,6 @@ Actual: {2} {0} ({1}) - - async TestMethod with UITestMethodAttribute are not supported. Either remove async or use TestMethodAttribute. - 不支持具有 UITestMethodAttribute 的异步 TestMethod。请删除异步或使用 TestMethodAttribute。 - - Property or method {0} on {1} return type is not assignable to 'IEnumerable'. {1} 返回类型上的属性或方法 {0} 不能分配给 "IEnumerable"。 @@ -297,18 +373,65 @@ Actual: {2} {1} 上的属性或方法 {0} 返回空 IEnumerable<object[]>。 - - UITestMethodAttribute.DispatcherQueue should not be null. To use UITestMethodAttribute within a WinUI Desktop App, remember to set the static UITestMethodAttribute.DispatcherQueue during the test initialization. - UITestMethodAttribute.DispatcherQueue 不应为 null。若要在 WinUI 桌面应用中使用 UITestMethodAttribute,请在测试初始化期间设置静态 UITestMethodAttribute.DispatcherQueue。 + + Actual value <{2}> is not greater than expected value <{1}>. {0} + 实际值 <{2}> 不大于预期值 <{1}>。{0} + + + + Actual value <{2}> is not greater than or equal to expected value <{1}>. {0} + 实际值 <{2}> 不大于或等于预期值 <{1}>。{0} + + + + Actual value <{2}> is not less than expected value <{1}>. {0} + 实际值 <{2}> 不小于预期值 <{1}>。{0} + + + + Actual value <{2}> is not less than or equal to expected value <{1}>. {0} + 实际值 <{2}> 不小于或等于预期值 <{1}>。{0} - - Type '{0}' is not assignable to '{1}'. - 类型“{0}”不能分配给“{1}”。 - - - {0} argument name like "applicationType" - - {1} fully qualified class name like "Microsoft.UI.Xaml.Application" - + + Expected value <{1}> to be positive. {0} + 预期值 <{1}> 为正值。{0} + + + + Expected value <{1}> to be negative. {0} + 预期值 <{1}> 为负值。{0} + + + + Assert.Equals should not be used for Assertions. Please use Assert.AreEqual & overloads instead. + Assert.Equals 不应用于断言。请改用 Assert.AreEqual 和重载。 + + + + Assert.ReferenceEquals should not be used for Assertions. Please use Assert.AreSame & overloads instead. + Assert.ReferenceEquals 不应用于断言。请改用 Assert.AreSame 和重载。 + + + + StringAssert.Equals should not be used for Assertions. Please use StringAssert methods or Assert.AreEqual & overloads instead. + StringAssert.Equals 不应用于断言。请改用 StringAssert 方法或 Assert.AreEqual 和重载。 + + + + StringAssert.ReferenceEquals should not be used for Assertions. Please use StringAssert methods or Assert.AreSame & overloads instead. + StringAssert.ReferenceEquals 不应用于断言。请改用 StringAssert 方法或 Assert.AreSame 和重载。 + + + + CollectionAssert.Equals should not be used for Assertions. Please use CollectionAssert.AreEqual & overloads instead. + CollectionAssert.Equals 不应用于断言。请改用 CollectionAssert.AreEqual 和重载。 + + + + CollectionAssert.ReferenceEquals should not be used for Assertions. Please use CollectionAssert methods or Assert.AreSame & overloads instead. + CollectionAssert.ReferenceEquals 不应用于断言。请改用 CollectionAssert 方法或 Assert.AreSame 和重载。 + diff --git a/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.zh-Hant.xlf b/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.zh-Hant.xlf index 49aa1e77f7..6433c45d7b 100644 --- a/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.zh-Hant.xlf +++ b/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.zh-Hant.xlf @@ -37,6 +37,37 @@ 預期: <{1} ({2})>。實際: <{3} ({4})>。{0} + + {0}{1} +{2} +{3} +{4} + {0}{1} +{2} +{3} +{4} + + + + Expected: + 預期為: + + + + But was: + 但為: + + + + String lengths are both {0} but differ at index {1}. + 字串長度兩者都是 {0},但索引 {1} 不同。 + + + + Expected string length {0} but was {1}. + 預期的字串長度為 {0},但為 {1}。 + + Expected any value except:<{1}>. Actual:<{2}>. {0} 預期任何值 (<{1}> 除外)。實際: <{2}>。{0} @@ -82,14 +113,59 @@ 字串 '{0}' 未包含字串 '{1}'。{2}。 + + Expected collection to contain the specified item. {0} + 預期集合包含指定的項目。{0} + + + + Expected at least one item to match the predicate. {0} + 必須至少有一個項目符合述詞。{0} + + + + Expected collection to contain exactly one element but found {1} element(s). {0} + 預期集合應僅包含一個元素,但發現 {1} 個元素。{0} + + + + Expected exactly one item to match the predicate but found {1} item(s). {0} + 預期只有一個項目符合述詞,但找到 {1} 個項目。{0} + + String '{0}' does contain string '{1}'. {2}. 字串 '{0}' 有包含字串 '{1}'。{2}。 + + Expected collection to not contain the specified item. {0} + 預期集合不包含指定的項目。{0} + + + + Expected no items to match the predicate. {0} + 預期沒有任何項目符合述詞。{0} + + + + String '{0}' ends with string '{1}'. {2} + 字串 '{0}' 以字串 '{1}' 結尾。{2} + + + + String '{0}' starts with string '{1}'. {2} + 字串 '{0}' 以字串 '{1}' 開頭。{2} + + + + Dynamic data field '{0}' should be static. + 動態資料欄位 '{0}' 應該為靜態。 + + - Dynamic data method '{0}' should be static, parameterless and non-generic. - 動態資料方法 '{0}' 應為靜態、無參數及非泛型。 + Dynamic data method '{0}' should be static, non-generic, and cannot have 'params' parameter. + 動態資料方法 '{0}' 應該是靜態、非一般,而且不能有 'params' 參數。 @@ -98,8 +174,8 @@ - The dynamic data source '{0}' in type '{1}' should exist and be a property or a method. - 類型 '{1}' 中的動態數據源 '{0}' 應該存在,而且必須是屬性或方法。 + The dynamic data source '{0}' in type '{1}' should exist and be a property, a method, or a field. + 類型 '{1}' 中的動態資料來源 '{0}' 應該存在,並且必須是屬性、方法或欄位。 @@ -122,9 +198,9 @@ Actual: {2} - String '{0}' does not end with string '{1}'. {2}. - 字串 '{0}' 不是以字串 '{1}' 結尾。{2}。 - + String '{0}' does not end with string '{1}'. {2} + 字串 '{0}' 不是以字串 '{1}' 結尾。{2} + {0} failed. {1} @@ -141,15 +217,20 @@ Actual: {2} 無效的 GitHub 票證 URL + + Value '{0}' is not within the expected range [{1}, {2}]. {3} + 值 '{0}' 不在預期的範圍 [{1}, {2}] 內。{3} + + {0} Expected type:<{1}>. Actual type:<{2}>. {0} 預期的類型: <{1}>,實際的類型: <{2}>。 - String '{0}' does not match pattern '{1}'. {2}. - 字串 '{0}' 與模式 '{1}' 不符。{2}。 - + String '{0}' does not match pattern '{1}'. {2} + 字串 '{0}' 與模式 '{1}' 不符。{2} + Expected collection to contain any item but it is empty. {0} @@ -162,14 +243,9 @@ Actual: {2} - String '{0}' matches pattern '{1}'. {2}. - 字串 '{0}' 與模式 '{1}' 相符。{2}。 - - - - Assert.Equals should not be used for Assertions. Please use Assert.AreEqual & overloads instead. - Assert.Equals 不應使用於判斷提示。請改用 Assert.AreEqual 及多載。 - + String '{0}' matches pattern '{1}'. {2} + 字串 '{0}' 與模式 '{1}' 相符。{2} + @@ -182,7 +258,7 @@ Actual: {2} 或者該成員可能為私用,並且定義在基底類別上。如果是後者, 您必須將定義該成員的類型傳遞至 PrivateObject 的建構函式。 - + @@ -195,7 +271,7 @@ Actual: {2} 或者該成員可能為私用,並且定義在基底類別上。如果是後者,您必須 將定義該成員的類型傳遞至 PrivateObject 的建構函式。 - + The parameter '{0}' is invalid. The value cannot be null. {1}. @@ -207,10 +283,15 @@ Actual: {2} 項目數目不同。 + + Element(s) <{0}> is/are not present in the collection. + Element(s) <{0}> is/are not present in the collection. + + - String '{0}' does not start with string '{1}'. {2}. - 字串 '{0}' 不是以字串 '{1}' 開頭。{2}。 - + String '{0}' does not start with string '{1}'. {2} + 字串 '{0}' 不是以字串 '{1}' 開頭。{2} + The property {0} has type {1}; expected type {2}. @@ -272,11 +353,6 @@ Actual: {2} {0} ({1}) - - async TestMethod with UITestMethodAttribute are not supported. Either remove async or use TestMethodAttribute. - 不支援有 UITestMethodAttribute 的 async TestMethod。請移除 async 或使用 TestMethodAttribute。 - - Property or method {0} on {1} return type is not assignable to 'IEnumerable'. {1} 傳回類型上的屬性或方法 {0} 無法指派給 'IEnumerable'。 @@ -297,18 +373,65 @@ Actual: {2} {1} 上的屬性或方法 {0} 傳回空的 IEnumerable<object[]>。 - - UITestMethodAttribute.DispatcherQueue should not be null. To use UITestMethodAttribute within a WinUI Desktop App, remember to set the static UITestMethodAttribute.DispatcherQueue during the test initialization. - UITestMethodAttribute.DispatcherQueue 不應為 Null。若要在 WinUI 傳統型應用程式內使用 UITestMethodAttribute,請記得在測試初始化期間設定靜態 UITestMethodAttribute.DispatcherQueue。 + + Actual value <{2}> is not greater than expected value <{1}>. {0} + 實際值 <{2}> 不大於預期值 <{1}>。{0} + + + + Actual value <{2}> is not greater than or equal to expected value <{1}>. {0} + 實際值 <{2}> 不大於或等於預期值 <{1}>。{0} + + + + Actual value <{2}> is not less than expected value <{1}>. {0} + 實際值 <{2}> 不小於預期值 <{1}>。{0} + + + + Actual value <{2}> is not less than or equal to expected value <{1}>. {0} + 實際值 <{2}> 不小於或等於預期值 <{1}>。{0} - - Type '{0}' is not assignable to '{1}'. - 無法將類型 '{0}' {0} 指派給 '{1}。 - - - {0} argument name like "applicationType" - - {1} fully qualified class name like "Microsoft.UI.Xaml.Application" - + + Expected value <{1}> to be positive. {0} + 預期值 <{1}> 為正數。{0} + + + + Expected value <{1}> to be negative. {0} + 預期值 <{1}> 為負數。{0} + + + + Assert.Equals should not be used for Assertions. Please use Assert.AreEqual & overloads instead. + Assert.Equals 不應使用於判斷提示。請改用 Assert.AreEqual 及多載。 + + + + Assert.ReferenceEquals should not be used for Assertions. Please use Assert.AreSame & overloads instead. + Assert.ReferenceEquals 不應使用於判斷提示。請改用 Assert.AreSame 及多載。 + + + + StringAssert.Equals should not be used for Assertions. Please use StringAssert methods or Assert.AreEqual & overloads instead. + StringAssert.Equals 不應使用於判斷提示。請改用 StringAssert 方法或 Assert.AreEqual 及其多載。 + + + + StringAssert.ReferenceEquals should not be used for Assertions. Please use StringAssert methods or Assert.AreSame & overloads instead. + StringAssert.ReferenceEquals 不應使用於判斷提示。請改用 StringAssert 方法或 Assert.AreSame 及其多載。 + + + + CollectionAssert.Equals should not be used for Assertions. Please use CollectionAssert.AreEqual & overloads instead. + CollectionAssert.Equals 不應使用於判斷提示。請改用 CollectionAssert.AreEqual 及多載。 + + + + CollectionAssert.ReferenceEquals should not be used for Assertions. Please use CollectionAssert methods or Assert.AreSame & overloads instead. + CollectionAssert.ReferenceEquals 不應使用於判斷提示。請改用 CollectionAssert 方法或 Assert.AreSame 及其多載。 + diff --git a/src/TestFramework/TestFramework/TestDataRow.cs b/src/TestFramework/TestFramework/TestDataRow.cs index bd271d9c69..ff3a433e2a 100644 --- a/src/TestFramework/TestFramework/TestDataRow.cs +++ b/src/TestFramework/TestFramework/TestDataRow.cs @@ -13,11 +13,13 @@ namespace Microsoft.VisualStudio.TestTools.UnitTesting; [DataContract] public sealed class TestDataRow : ITestDataRow { +#pragma warning disable CS1574 // XML comment has cref attribute that could not be resolved /// /// Initializes a new instance of the class. /// - /// The value to be held by this instance, which could be a or if the test method has more than one parameter. + /// The value to be held by this instance, which could be a or if the test method has more than one parameter. public TestDataRow(T value) +#pragma warning restore CS1574 // XML comment has cref attribute that could not be resolved => Value = value; /// @@ -38,6 +40,12 @@ public TestDataRow(T value) [DataMember] public string? DisplayName { get; set; } + /// + /// Gets or sets the test categories for the test case. + /// + [DataMember] + public IList? TestCategories { get; set; } + /// object? ITestDataRow.Value => Value; } diff --git a/src/TestFramework/TestFramework/TestFramework.csproj b/src/TestFramework/TestFramework/TestFramework.csproj index a48363f7b4..d1c919233b 100644 --- a/src/TestFramework/TestFramework/TestFramework.csproj +++ b/src/TestFramework/TestFramework/TestFramework.csproj @@ -8,7 +8,7 @@ Microsoft.VisualStudio.TestTools.UnitTesting Microsoft.VisualStudio.TestPlatform.TestFramework - TRACE + $(DefineConstants);TRACE @@ -33,27 +33,19 @@ - - - - - - - True - True - FrameworkMessages.resx - - - ResXFileCodeGenerator - FrameworkMessages.Designer.cs - Microsoft.VisualStudio.TestTools.UnitTesting - Designer - + + + + + + + + diff --git a/test/.editorconfig b/test/.editorconfig index 4ffa71b86f..58ccc42e50 100644 --- a/test/.editorconfig +++ b/test/.editorconfig @@ -25,6 +25,10 @@ dotnet_diagnostic.CA1822.severity = none # CA1822: Mark members as st dotnet_diagnostic.CA1852.severity = none # CA1852: Type can be sealed dotnet_diagnostic.CA1859.severity = none # CA1859: Change return type to be more specific dotnet_diagnostic.CA1861.severity = none # CA1861: Avoid constant arrays as arguments + +# CA2007: Consider calling ConfigureAwait on the awaited task +dotnet_diagnostic.CA2007.severity = none + dotnet_diagnostic.CA2201.severity = none # CA2201: Do not raise reserved exception types dotnet_diagnostic.CA3075.severity = none # CA3075: Insecure DTD processing in XML diff --git a/test/Directory.Build.props b/test/Directory.Build.props index 7b462d3b5c..09674c87fb 100644 --- a/test/Directory.Build.props +++ b/test/Directory.Build.props @@ -16,6 +16,8 @@ $(DefineConstants);ENABLE_CODECOVERAGE false + false + $(NoWarn);SA0001;EnableGenerateDocumentationFile diff --git a/test/Directory.Build.targets b/test/Directory.Build.targets index 2980dfd99e..83503ee774 100644 --- a/test/Directory.Build.targets +++ b/test/Directory.Build.targets @@ -3,31 +3,33 @@ - - true + false 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 - + Recommended - + $(TestingPlatformCommandLineArguments) --diagnostic --diagnostic-output-directory $(RepoRoot)artifacts/log/$(Configuration) --diagnostic-output-fileprefix $(ModuleName) --diagnostic-verbosity trace + $(TestingPlatformCommandLineArguments) --crashdump + $(TestingPlatformCommandLineArguments) --hangdump --hangdump-timeout 15m + $(TestingPlatformCommandLineArguments) --report-azdo + $(TestingPlatformCommandLineArguments) --coverage --coverage-settings $(RepoRoot)test/coverage.config --coverage-output $(ModuleName).coverage + + + + + + + + + + <_TestArchitecture>$(PlatformTarget) <_TestArchitecture Condition="'$(PlatformTarget)' == '' or '$(PlatformTarget)' == 'AnyCpu'">x64 - - <_ResultFileNameNoExt>$(MSBuildProjectName)_$(TargetFramework)_$(_TestArchitecture) - $(ArtifactsTestResultsDir)$(_ResultFileNameNoExt).trx - <_TestResultTrxFileName>$([System.IO.Path]::GetFileName('$(ResultsTrxPath)')) - <_TestResultDirectory>$([System.IO.Path]::GetDirectoryName('$(ResultsTrxPath)')) - $(TestingPlatformCommandLineArguments) --report-trx --report-trx-filename "$(_TestResultTrxFileName)" --results-directory "$(_TestResultDirectory)" --report-azdo $(TestRunnerAdditionalArguments) - + $(TestingPlatformCommandLineArguments) --report-trx --report-trx-filename $(MSBuildProjectName)_$(TargetFramework)_$(_TestArchitecture).trx --results-directory $(ArtifactsTestResultsDir) @@ -47,7 +49,6 @@ - @@ -57,8 +58,7 @@ - + diff --git a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/AbortionTests.cs b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/AbortionTests.cs index d2fd2c24e5..15bd3a545f 100644 --- a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/AbortionTests.cs +++ b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/AbortionTests.cs @@ -28,10 +28,12 @@ public async Task AbortWithCTRLPlusC_CancellingTests(string tfm) string fileCreationPath = Path.Combine(testHost.DirectoryName, "fileCreation"); File.WriteAllText(fileCreationPath, string.Empty); - TestHostResult testHostResult = await testHost.ExecuteAsync(environmentVariables: new() - { - ["FILE_DIRECTORY"] = fileCreationPath, - }); + TestHostResult testHostResult = await testHost.ExecuteAsync( + environmentVariables: new() + { + ["FILE_DIRECTORY"] = fileCreationPath, + }, + cancellationToken: TestContext.CancellationToken); testHostResult.AssertExitCodeIs(ExitCodes.TestSessionAborted); @@ -159,4 +161,6 @@ public async Task TestA() .PatchCodeWithReplace("$MSTestVersion$", MSTestVersion)); } } + + public TestContext TestContext { get; set; } } diff --git a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/AnalyzersTests.cs b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/AnalyzersTests.cs index b7db7f3140..f88e6afbda 100644 --- a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/AnalyzersTests.cs +++ b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/AnalyzersTests.cs @@ -9,6 +9,66 @@ namespace MSTest.Acceptance.IntegrationTests; [TestClass] public sealed class AnalyzersTests : AcceptanceTestBase { + [TestMethod] + [DataRow(false, false)] + [DataRow(false, true)] + [DataRow(true, false)] + [DataRow(true, true)] + public async Task MSTEST0001(bool isMTP, bool isAdapterReferenced) + { + string code = $$""" +#file TestForMSTEST0001.csproj + + + + $TargetFrameworks$ + true + all + + {{(isMTP ? """ + true + Exe + """ : string.Empty)}} + + + + + + {{(isAdapterReferenced ? "" : string.Empty)}} + + + +#file UnitTest1.cs +using Microsoft.VisualStudio.TestTools.UnitTesting; + +[TestClass] +public class UnitTest1 +{ + [TestMethod] + public void TestMethod() + { + } +} +""".PatchTargetFrameworks(TargetFrameworks.NetCurrent) + .PatchCodeWithReplace("$MSTestVersion$", MSTestVersion) + .PatchCodeWithReplace("$MicrosoftNETTestSdkVersion$", MicrosoftNETTestSdkVersion); + + using TestAsset testAsset = await TestAsset.GenerateAssetAsync("TestForMSTEST0001", code); + DotnetMuxerResult result = await DotnetCli.RunAsync( + $"build {testAsset.TargetAssetPath}", + AcceptanceFixture.NuGetGlobalPackagesFolder.Path, + warnAsError: false, + cancellationToken: TestContext.CancellationToken); + if (isAdapterReferenced) + { + result.AssertOutputContains("warning MSTEST0001"); + } + else + { + result.AssertOutputDoesNotContain("warning MSTEST0001"); + } + } + [TestMethod] public async Task AnalyzersShouldBeEnabledWhenUsingMetapackage() { @@ -51,7 +111,11 @@ public void TestMethod() .PatchCodeWithReplace("$MSTestVersion$", MSTestVersion); using TestAsset testAsset = await TestAsset.GenerateAssetAsync("AnalyzersMetapackage", code); - DotnetMuxerResult result = await DotnetCli.RunAsync($"build {testAsset.TargetAssetPath}", AcceptanceFixture.NuGetGlobalPackagesFolder.Path, warnAsError: false); + DotnetMuxerResult result = await DotnetCli.RunAsync( + $"build {testAsset.TargetAssetPath}", + AcceptanceFixture.NuGetGlobalPackagesFolder.Path, + warnAsError: false, + cancellationToken: TestContext.CancellationToken); result.AssertOutputContains("MSTEST0014"); } @@ -87,7 +151,11 @@ public void TestMethod() .PatchCodeWithReplace("$MSTestVersion$", MSTestVersion); using TestAsset testAsset = await TestAsset.GenerateAssetAsync("AnalyzersTestFrameworkPackage", code); - DotnetMuxerResult result = await DotnetCli.RunAsync($"build {testAsset.TargetAssetPath}", AcceptanceFixture.NuGetGlobalPackagesFolder.Path, warnAsError: false); + DotnetMuxerResult result = await DotnetCli.RunAsync( + $"build {testAsset.TargetAssetPath}", + AcceptanceFixture.NuGetGlobalPackagesFolder.Path, + warnAsError: false, + cancellationToken: TestContext.CancellationToken); result.AssertOutputContains("MSTEST0014"); } @@ -126,12 +194,17 @@ public void TestMethod() .PatchCodeWithReplace("$MSTestVersion$", MSTestVersion); using TestAsset testAsset = await TestAsset.GenerateAssetAsync("Analyzers", code); - DotnetMuxerResult result = await DotnetCli.RunAsync($"build {testAsset.TargetAssetPath}", AcceptanceFixture.NuGetGlobalPackagesFolder.Path, environmentVariables: new() - { - ["DOTNET_CLI_UI_LANGUAGE"] = "it-IT", - ["PreferredUILang"] = "it-IT", - ["VSLang"] = "1040", - }, warnAsError: false); + DotnetMuxerResult result = await DotnetCli.RunAsync( + $"build {testAsset.TargetAssetPath}", + AcceptanceFixture.NuGetGlobalPackagesFolder.Path, + environmentVariables: new() + { + ["DOTNET_CLI_UI_LANGUAGE"] = "it-IT", + ["PreferredUILang"] = "it-IT", + ["VSLang"] = "1040", + }, + warnAsError: false, + cancellationToken: TestContext.CancellationToken); result.AssertOutputContains("DataRow deve essere impostato solo su un metodo di test"); } @@ -233,12 +306,14 @@ private static async Task AssertAnalysisModeAsync(string mode, string[] contains foreach (string containsElement in contains) { - StringAssert.Contains(output, containsElement, $"Expected to find '{containsElement}' for analysisMode {mode}"); + Assert.Contains(containsElement, output, $"Expected to find '{containsElement}' for analysisMode {mode}"); } foreach (string doesNotContainElement in doesNotContain) { - Assert.IsFalse(output.Contains(doesNotContainElement), $"Expected to not find '{doesNotContainElement}' for analysisMode {mode}"); + Assert.DoesNotContain(doesNotContainElement, output, $"Expected to not find '{doesNotContainElement}' for analysisMode {mode}"); } } + + public TestContext TestContext { get; set; } } diff --git a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/AssemblyResolutionTests.cs b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/AssemblyResolutionTests.cs index 1cad3bfabc..304100de45 100644 --- a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/AssemblyResolutionTests.cs +++ b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/AssemblyResolutionTests.cs @@ -12,7 +12,7 @@ public sealed class AssemblyResolutionTests : AcceptanceTestBase +{ + [TestMethod] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] + public async Task DeadlockCaseClassCleanupWaitingOnTestMethod(string tfm) + { + var testHost = TestHost.LocateFrom(AssetFixture.ProjectPath, TestAssetFixture.ProjectName, tfm); + TestHostResult testHostResult = await testHost.ExecuteAsync("--filter FullyQualifiedName~DeadlockCase1", cancellationToken: TestContext.CancellationToken); + + testHostResult.AssertExitCodeIs(0); + testHostResult.AssertOutputContainsSummary(failed: 0, passed: 1, skipped: 0); + } + + [TestMethod] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] + public async Task DeadlockCaseClassInitWaitingOnPreviousTestMethod(string tfm) + { + var testHost = TestHost.LocateFrom(AssetFixture.ProjectPath, TestAssetFixture.ProjectName, tfm); + TestHostResult testHostResult = await testHost.ExecuteAsync("--filter FullyQualifiedName~DeadlockCase2", cancellationToken: TestContext.CancellationToken); + + testHostResult.AssertExitCodeIs(0); + testHostResult.AssertOutputContainsSummary(failed: 0, passed: 2, skipped: 0); + } + + public sealed class TestAssetFixture() : TestAssetFixtureBase(AcceptanceFixture.NuGetGlobalPackagesFolder) + { + public const string ProjectName = "DeadlockTests"; + + public string ProjectPath => GetAssetPath(ProjectName); + + public override IEnumerable<(string ID, string Name, string Code)> GetAssetsToGenerate() + { + yield return (ProjectName, ProjectName, + SourceCode + .PatchTargetFrameworks(TargetFrameworks.All) + .PatchCodeWithReplace("$MSTestVersion$", MSTestVersion)); + } + + private const string SourceCode = """ +#file DeadlockTests.csproj + + + + Exe + true + $TargetFrameworks$ + preview + + + true + + + + + + + + +#file UnitTest1.cs +using System; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +[TestClass] +public class DeadlockCase1 +{ + // This repro is intended to deadlock when Test1 is called asynchronously like await Test1(), but then ClassCleanup is called with GetAwaiter().GetResult(). + // When await Test1() is called, we return a Task that will complete in ~1second on a custom thread we created. + // After that Task completes, the continuation will run on that exact same custom thread. + // If the continuation calls ClassCleanup().GetAwaiter().GetResult(), then we are blocking that custom thread waiting for ClassCleanup to complete. + // But ClassCleanup cannot complete until that custom thread calls _cts.SetResult(), which it cannot do because it is blocked waiting for ClassCleanup to complete. + // So we deadlock. + // This repro is related to https://github.com/microsoft/testfx/issues/6575 + private static TaskCompletionSource _cts = new(); + + [ClassCleanup(ClassCleanupBehavior.EndOfClass)] + public static async Task ClassCleanup() + { + await _cts.Task; + } + + [TestMethod] + public async Task Test1() + { + var cts1 = new TaskCompletionSource(); + var t = new Thread(() => + { + Thread.Sleep(1000); + cts1.SetResult(null); + Thread.Sleep(1000); + _cts.SetResult(null); + }); + t.Start(); + await cts1.Task; + } +} + +[TestClass] +public class DeadlockCase2 +{ + // This repro is intended to deadlock when Test1 is called asynchronously like await Test1(), but then TestInit (the invocation before Test2 and after Test1) is called with GetAwaiter().GetResult(). + // When await Test1() is called, we return a Task that will complete in ~1second on a custom thread we created. + // After that Task completes, the continuation will run on that exact same custom thread. + // If the continuation calls TestInit().GetAwaiter().GetResult(), then we are blocking that custom thread waiting for TestInit to complete. + // But TestInit cannot complete until that custom thread calls _cts.SetResult(), which it cannot do because it is blocked waiting for TestInit to complete. + // So we deadlock. + // This repro is related to https://github.com/microsoft/testfx/issues/6575 + private static TaskCompletionSource _cts = new(); + + public TestContext TestContext { get; set; } + + [TestInitialize] + public async Task TestInit() + { + if (TestContext.TestName == nameof(Test2)) + { + await _cts.Task; + } + } + + [TestMethod] + public async Task Test1() + { + var cts1 = new TaskCompletionSource(); + var t = new Thread(() => + { + Thread.Sleep(1000); + cts1.SetResult(null); + Thread.Sleep(1000); + _cts.SetResult(null); + }); + t.Start(); + await cts1.Task; + } + + [TestMethod] + public void Test2() + { + } +} + +"""; + } + + public TestContext TestContext { get; set; } +} diff --git a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/DotnetTestCliTests.cs b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/DotnetTestCliTests.cs index 866bd1ed8f..0b1f8ebca7 100644 --- a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/DotnetTestCliTests.cs +++ b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/DotnetTestCliTests.cs @@ -12,7 +12,7 @@ public class DotnetTestCliTests : AcceptanceTestBase private const string AssetName = "MSTestProject"; [TestMethod] - [DynamicData(nameof(GetBuildMatrixTfmBuildConfiguration), typeof(AcceptanceTestBase), DynamicDataSourceType.Method)] + [DynamicData(nameof(GetBuildMatrixTfmBuildConfiguration), typeof(AcceptanceTestBase))] public async Task DotnetTest_Should_Execute_Tests(string tfm, BuildConfiguration buildConfiguration) { using TestAsset generator = await TestAsset.GenerateAssetAsync( @@ -25,9 +25,11 @@ public async Task DotnetTest_Should_Execute_Tests(string tfm, BuildConfiguration .PatchCodeWithReplace("$OutputType$", string.Empty) .PatchCodeWithReplace("$Extra$", string.Empty)); - DotnetMuxerResult compilationResult = await DotnetCli.RunAsync($"test -m:1 -nodeReuse:false {generator.TargetAssetPath}", AcceptanceFixture.NuGetGlobalPackagesFolder.Path, workingDirectory: generator.TargetAssetPath); + DotnetMuxerResult compilationResult = await DotnetCli.RunAsync($"test -m:1 -nodeReuse:false {generator.TargetAssetPath}", AcceptanceFixture.NuGetGlobalPackagesFolder.Path, workingDirectory: generator.TargetAssetPath, cancellationToken: TestContext.CancellationToken); // There is whitespace difference in output in parent and public repo that depends on the version of the dotnet SDK used. compilationResult.AssertOutputMatchesRegex(@"Passed!\s+-\s+Failed:\s+0,\s+Passed:\s+1,\s+Skipped:\s+0,\s+Total:\s+1"); } + + public TestContext TestContext { get; set; } } diff --git a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/DynamicDataMethodTests.cs b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/DynamicDataMethodTests.cs new file mode 100644 index 0000000000..b2d4f991ba --- /dev/null +++ b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/DynamicDataMethodTests.cs @@ -0,0 +1,186 @@ +// 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; + +[TestClass] +public sealed class DynamicDataMethodTests : AcceptanceTestBase +{ + [TestMethod] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] + public async Task DynamicDataTestWithParameterizedDataProviderMethod(string tfm) + { + var testHost = TestHost.LocateFrom(AssetFixture.ProjectPath, TestAssetFixture.ProjectName, tfm); + TestHostResult testHostResult = await testHost.ExecuteAsync("--settings my.runsettings", cancellationToken: TestContext.CancellationToken); + + testHostResult.AssertExitCodeIs(ExitCodes.AtLeastOneTestFailed); + testHostResult.AssertOutputContainsSummary(failed: 3, passed: 9, skipped: 0); + + // failed TestMethodSingleParameterIntCountMismatchSmaller (0ms) + // Parameter count mismatch. + // at System.Reflection.MethodBaseInvoker.ThrowTargetParameterCountException() + // at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture) + // at System.Reflection.MethodBase.Invoke(Object obj, Object[] parameters) + // + // failed TestMethodSingleParameterIntCountMismatchLarger (0ms) + // Parameter count mismatch. + // at System.Reflection.MethodBaseInvoker.ThrowTargetParameterCountException() + // at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture) + // at System.Reflection.MethodBase.Invoke(Object obj, Object[] parameters) + // + // failed TestMethodParamsNotSupported (0ms) + // Dynamic data method 'TestClass1.GetDataParams' should be static, non-generic, and cannot have 'params' parameter. + testHostResult.AssertOutputMatchesRegex(@"failed TestMethodSingleParameterIntCountMismatchSmaller \(\d+ms\)[\r\n]+\s+Parameter count mismatch."); + testHostResult.AssertOutputMatchesRegex(@"failed TestMethodSingleParameterIntCountMismatchLarger \(\d+ms\)[\r\n]+\s+Parameter count mismatch."); + testHostResult.AssertOutputMatchesRegex(@"failed TestMethodParamsNotSupported \(\d+ms\)[\r\n]+\s+Dynamic data method 'TestClass1.GetDataParams' should be static, non-generic, and cannot have 'params' parameter."); + + testHostResult.AssertOutputContains("TestMethodSingleParameterInt called with: 4"); + testHostResult.AssertOutputContains("TestMethodSingleParameterInt called with: 5"); + testHostResult.AssertOutputContains("TestMethodSingleParameterInt called with: 6"); + + testHostResult.AssertOutputContains("TestMethodTwoParametersIntAndString called with: 4, Hello1"); + testHostResult.AssertOutputContains("TestMethodTwoParametersIntAndString called with: 5, Hello2"); + testHostResult.AssertOutputContains("TestMethodTwoParametersIntAndString called with: 6, Hello3"); + + testHostResult.AssertOutputContains("TestMethodSingleParameterIntArray called with: 5"); + testHostResult.AssertOutputContains("TestMethodSingleParameterIntArray called with: 7"); + testHostResult.AssertOutputContains("TestMethodSingleParameterIntArray called with: 9"); + } + + public sealed class TestAssetFixture() : TestAssetFixtureBase(AcceptanceFixture.NuGetGlobalPackagesFolder) + { + public const string ProjectName = "DynamicDataMethodTests"; + + public string ProjectPath => GetAssetPath(ProjectName); + + public override IEnumerable<(string ID, string Name, string Code)> GetAssetsToGenerate() + { + yield return (ProjectName, ProjectName, + SourceCode + .PatchTargetFrameworks(TargetFrameworks.All) + .PatchCodeWithReplace("$MSTestVersion$", MSTestVersion)); + } + + private const string SourceCode = """ +#file DynamicDataMethodTests.csproj + + + + Exe + true + $TargetFrameworks$ + preview + + + + + + + + + + PreserveNewest + + + + +#file TestClass1.cs +using System; +using System.Collections.Generic; +using System.Text; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +[TestClass] +public class TestClass1 +{ + [TestMethod] + [DynamicData(nameof(GetDataSingleParameterInt), 4)] + public void TestMethodSingleParameterInt(int a) + { + // We call GetDataSingleParameterInt with 4, so it should yield 4, 5, and 6. + Console.WriteLine($"TestMethodSingleParameterInt called with: {a}"); + } + + [TestMethod] + [DynamicData(nameof(GetDataTwoParametersIntAndString), 4, "Hello")] + public void TestMethodTwoParametersIntAndString(int a, string s) + { + // We call GetDataTwoParametersIntAndString with 4 and "Hello", so it should yield: + // - (4, "Hello1") + // - (5, "Hello2") + // - (6, "Hello3") + Console.WriteLine($"TestMethodTwoParametersIntAndString called with: {a}, {s}"); + } + + [TestMethod] + [DynamicData(nameof(GetDataSingleParameterIntArray), [new int[] { 4, 5, 6 }])] + public void TestMethodSingleParameterIntArray(int x) + { + // We call GetDataSingleParameterIntArray with an array (4, 5, and 6), so it should yield 5, 7, and 9. + Console.WriteLine($"TestMethodSingleParameterIntArray called with: {x}"); + } + + [TestMethod] + [DynamicData(nameof(GetDataSingleParameterInt))] + public void TestMethodSingleParameterIntCountMismatchSmaller(int x) + { + // This test should fail due parameter count mismatch. + } + + [TestMethod] + [DynamicData(nameof(GetDataSingleParameterInt), 1, 2)] + public void TestMethodSingleParameterIntCountMismatchLarger(int x) + { + // This test should fail due parameter count mismatch. + } + + [TestMethod] + [DynamicData(nameof(GetDataParams), 1, 2)] + public void TestMethodParamsNotSupported(int x) + { + // This test should fail because we don't support params. + } + + public static IEnumerable GetDataSingleParameterInt(int i) + { + yield return i++; + yield return i++; + yield return i++; + } + + public static IEnumerable GetDataSingleParameterIntArray(int[] input) + { + yield return [1 + input[0]]; + yield return [2 + input[1]]; + yield return [3 + input[2]]; + } + + public static IEnumerable GetDataTwoParametersIntAndString(int i, string s) + { + yield return new object[] { i++, s + "1" }; + yield return new object[] { i++, s + "2" }; + yield return new object[] { i++, s + "3" }; + } + + public static IEnumerable GetDataParams(params int[] i) + { + yield return 0; + yield return 1; + } +} + +#file my.runsettings + + + false + + +"""; + } + + public TestContext TestContext { get; set; } +} diff --git a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/FrameworkOnlyTests.cs b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/FrameworkOnlyTests.cs index 12c1448218..f191ad5c46 100644 --- a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/FrameworkOnlyTests.cs +++ b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/FrameworkOnlyTests.cs @@ -19,7 +19,7 @@ public async Task DynamicDataAttributeGetDataShouldWorkWithoutAdapter() // Users shouldn't need to reference adapter, nor do anything // special, to be able to call DynamicData.GetData. var testHost = TestHost.LocateFrom(AssetFixture.TargetAssetPath, AssetName, TargetFrameworks.NetCurrent); - TestHostResult testHostResult = await testHost.ExecuteAsync(); + TestHostResult testHostResult = await testHost.ExecuteAsync(cancellationToken: TestContext.CancellationToken); testHostResult.AssertOutputContains(""" 1,2 3,4 @@ -93,4 +93,6 @@ public void TestMethod1() .PatchCodeWithReplace("$MSTestVersion$", MSTestVersion)); } } + + public TestContext TestContext { get; set; } } diff --git a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/GenericTestMethodTests.cs b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/GenericTestMethodTests.cs index c298349c7a..0222401ac1 100644 --- a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/GenericTestMethodTests.cs +++ b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/GenericTestMethodTests.cs @@ -15,64 +15,60 @@ public async Task TestDifferentGenericMethodTestCases() { var testHost = TestHost.LocateFrom(AssetFixture.GetAssetPath("GenericTestMethodTests"), "GenericTestMethodTests", TargetFrameworks.NetCurrent); - TestHostResult testHostResult = await testHost.ExecuteAsync(); + TestHostResult testHostResult = await testHost.ExecuteAsync(cancellationToken: TestContext.CancellationToken); testHostResult.AssertExitCodeIs(ExitCodes.AtLeastOneTestFailed); testHostResult.AssertOutputMatchesRegex( """ - failed AMethodWithBadConstraints \(0\) \(\d+ms\) + failed AMethodWithBadConstraints \(0\) \((\d+s )?\d+ms\) GenericArguments\[0], 'System\.Int32', on 'Void AMethodWithBadConstraints\[T]\(T\)' violates the constraint of type 'T'\. .+? - failed NonParameterizedTestMethod \(\d+ms\) + failed NonParameterizedTestMethod \((\d+s )?\d+ms\) The generic test method 'NonParameterizedTestMethod' doesn't have arguments, so the generic parameter cannot be inferred\. .+? - failed ParameterizedMethodSimple \(1\) \(\d+ms\) + failed ParameterizedMethodSimple \(1\) \((\d+s )?\d+ms\) Assert\.Fail failed\. Test method 'ParameterizedMethodSimple' did run with parameter '1' and type 'System\.Byte'\. .+? - failed ParameterizedMethodSimple \(2\) \(\d+ms\) + failed ParameterizedMethodSimple \(2\) \((\d+s )?\d+ms\) Assert\.Fail failed\. Test method 'ParameterizedMethodSimple' did run with parameter '2' and type 'System\.Int32'\. .+? - failed ParameterizedMethodSimple \("Hello world"\) \(\d+ms\) + failed ParameterizedMethodSimple \("Hello world"\) \((\d+s )?\d+ms\) Assert\.Fail failed\. Test method 'ParameterizedMethodSimple' did run with parameter 'Hello world' and type 'System\.String'\. .+? - failed ParameterizedMethodSimple \(null\) \(\d+ms\) + failed ParameterizedMethodSimple \(null\) \((\d+s )?\d+ms\) Test method TestClass\.ParameterizedMethodSimple threw exception: System\.InvalidOperationException: The type of the generic parameter 'T' could not be inferred\. - .+? - failed ParameterizedMethodTwoGenericParametersAndFourMethodParameters \(1,"Hello world",2,3\) \(\d+ms\) + failed ParameterizedMethodTwoGenericParametersAndFourMethodParameters \(1,"Hello world",2,3\) \((\d+s )?\d+ms\) Test method TestClass\.ParameterizedMethodTwoGenericParametersAndFourMethodParameters threw exception: System\.InvalidOperationException: Found two conflicting types for generic parameter 'T2'\. The conflicting types are 'System\.Byte' and 'System\.Int32'\. - .+? - failed ParameterizedMethodTwoGenericParametersAndFourMethodParameters \(null,"Hello world","Hello again",3\) \(\d+ms\) + failed ParameterizedMethodTwoGenericParametersAndFourMethodParameters \(null,"Hello world","Hello again",3\) \((\d+s )?\d+ms\) Assert\.Fail failed\. Test method 'ParameterizedMethodTwoGenericParametersAndFourMethodParameters' did run with parameters '', 'Hello world', 'Hello again', '3' and generic types 'System\.Int32', 'System\.String'\. .+? - failed ParameterizedMethodTwoGenericParametersAndFourMethodParameters \("Hello hello","Hello world",null,null\) \(\d+ms\) + failed ParameterizedMethodTwoGenericParametersAndFourMethodParameters \("Hello hello","Hello world",null,null\) \((\d+s )?\d+ms\) Test method TestClass\.ParameterizedMethodTwoGenericParametersAndFourMethodParameters threw exception: System\.InvalidOperationException: The type of the generic parameter 'T1' could not be inferred\. - .+? - failed ParameterizedMethodTwoGenericParametersAndFourMethodParameters \(null,null,null,null\) \(\d+ms\) + failed ParameterizedMethodTwoGenericParametersAndFourMethodParameters \(null,null,null,null\) \((\d+s )?\d+ms\) Test method TestClass\.ParameterizedMethodTwoGenericParametersAndFourMethodParameters threw exception: System\.InvalidOperationException: The type of the generic parameter 'T1' could not be inferred\. - .+? - failed ParameterizedMethodSimpleParams \(1\) \(\d+ms\) + failed ParameterizedMethodSimpleParams \(1\) \((\d+s )?\d+ms\) Cannot create an instance of T\[] because Type\.ContainsGenericParameters is true\. .+? - failed ParameterizedMethodSimpleParams \(1,2\) \(\d+ms\) + failed ParameterizedMethodSimpleParams \(1,2\) \((\d+s )?\d+ms\) Cannot create an instance of T\[] because Type\.ContainsGenericParameters is true\. .+? - failed ParameterizedMethodSimpleParams \("Hello world"\) \(\d+ms\) + failed ParameterizedMethodSimpleParams \("Hello world"\) \((\d+s )?\d+ms\) Cannot create an instance of T\[] because Type\.ContainsGenericParameters is true\. .+? - failed ParameterizedMethodSimpleParams \(null\) \(\d+ms\) + failed ParameterizedMethodSimpleParams \(null\) \((\d+s )?\d+ms\) Cannot create an instance of T\[] because Type\.ContainsGenericParameters is true\. .+? - failed ParameterizedMethodSimpleParams \(null,"Hello world"\) \(\d+ms\) + failed ParameterizedMethodSimpleParams \(null,"Hello world"\) \((\d+s )?\d+ms\) Cannot create an instance of T\[] because Type\.ContainsGenericParameters is true\. .+? - failed ParameterizedMethodWithNestedGeneric \(System\.Collections\.Generic\.List`1\[System.String],System\.Collections\.Generic\.List`1\[System.String]\) \(\d+ms\) + failed ParameterizedMethodWithNestedGeneric \(System\.Collections\.Generic\.List`1\[System.String],System\.Collections\.Generic\.List`1\[System.String]\) \((\d+s )?\d+ms\) Assert\.Fail failed\. Test method 'ParameterizedMethodWithNestedGeneric' did run with first list \[Hello, World] and second list \[Unit, Testing] .+? - failed ParameterizedMethodWithNestedGeneric \(System\.Collections\.Generic\.List`1\[System.Int32],System\.Collections\.Generic\.List`1\[System.Int32]\) \(\d+ms\) + failed ParameterizedMethodWithNestedGeneric \(System\.Collections\.Generic\.List`1\[System.Int32],System\.Collections\.Generic\.List`1\[System.Int32]\) \((\d+s )?\d+ms\) Assert\.Fail failed\. Test method 'ParameterizedMethodWithNestedGeneric' did run with first list \[0, 1] and second list \[2, 3] .+? """, RegexOptions.Singleline); @@ -174,4 +170,6 @@ public static IEnumerable Data } """; } + + public TestContext TestContext { get; set; } } diff --git a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/HelpInfoTests.cs b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/HelpInfoTests.cs index d43193af01..5a74f90618 100644 --- a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/HelpInfoTests.cs +++ b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/HelpInfoTests.cs @@ -17,7 +17,7 @@ public class HelpInfoTests : AcceptanceTestBase public async Task Help_WhenMSTestExtensionRegistered_OutputHelpContentOfRegisteredExtension(string tfm) { var testHost = TestHost.LocateFrom(AssetFixture.TargetAssetPath, AssetName, tfm); - TestHostResult testHostResult = await testHost.ExecuteAsync("--help"); + TestHostResult testHostResult = await testHost.ExecuteAsync("--help", cancellationToken: TestContext.CancellationToken); testHostResult.AssertExitCodeIs(ExitCodes.Success); @@ -28,6 +28,8 @@ Execute a .NET Test Application. Options: --config-file Specifies a testconfig.json file. + --debug + Allows to pause execution in order to attach to the process for debug purposes. --diagnostic Enable the diagnostic logging. The default log level is 'Trace'. The file will be written in the output directory with the name log_[yyMMddHHmmssfff].diag @@ -45,6 +47,8 @@ Output directory of the diagnostic logging. 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. + --filter-uid + Provides a list of test node UIDs to filter by. --help Show the command line help. --ignore-exit-code @@ -89,7 +93,7 @@ Output verbosity when reporting tests. public async Task Info_WhenMSTestExtensionRegistered_OutputInfoContentOfRegisteredExtension(string tfm) { var testHost = TestHost.LocateFrom(AssetFixture.TargetAssetPath, AssetName, tfm); - TestHostResult testHostResult = await testHost.ExecuteAsync("--info"); + TestHostResult testHostResult = await testHost.ExecuteAsync("--info", cancellationToken: TestContext.CancellationToken); testHostResult.AssertExitCodeIs(ExitCodes.Success); @@ -162,4 +166,6 @@ public void Test() {} } """; } + + public TestContext TestContext { get; set; } } diff --git a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/IgnoreTests.cs b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/IgnoreTests.cs index e173269e32..615f7748fe 100644 --- a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/IgnoreTests.cs +++ b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/IgnoreTests.cs @@ -14,7 +14,7 @@ public sealed class IgnoreTests : AcceptanceTestBase Data2 } """; } + + public TestContext TestContext { get; set; } } diff --git a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/InconclusiveTests.cs b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/InconclusiveTests.cs index 4e6678c47b..26b4cc714e 100644 --- a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/InconclusiveTests.cs +++ b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/InconclusiveTests.cs @@ -37,7 +37,8 @@ public async Task TestOutcomeShouldBeRespectedCorrectly(Lifecycle inconclusiveSt environmentVariables: new Dictionary { [$"{inconclusiveStep}Inconclusive"] = "1", - }); + }, + cancellationToken: TestContext.CancellationToken); if (inconclusiveStep >= Lifecycle.ClassCleanup) { @@ -232,4 +233,6 @@ public static void AsmCleanup(TestContext _) } """; } + + public TestContext TestContext { get; set; } } diff --git a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/LeakTests.cs b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/LeakTests.cs new file mode 100644 index 0000000000..ddfe696468 --- /dev/null +++ b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/LeakTests.cs @@ -0,0 +1,135 @@ +// 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; + +[TestClass] +public sealed class LeakTests : AcceptanceTestBase +{ + [TestMethod] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] + public async Task TestContextInstancesShouldNotLeak(string tfm) + { + var testHost = TestHost.LocateFrom(AssetFixture.ProjectPath, TestAssetFixture.ProjectName, tfm); + TestHostResult testHostResult = await testHost.ExecuteAsync(cancellationToken: TestContext.CancellationToken); + + testHostResult.AssertExitCodeIs(0); + testHostResult.AssertOutputContainsSummary(failed: 0, passed: 100, skipped: 0); + } + + public sealed class TestAssetFixture() : TestAssetFixtureBase(AcceptanceFixture.NuGetGlobalPackagesFolder) + { + public const string ProjectName = "LeakTests"; + + public string ProjectPath => GetAssetPath(ProjectName); + + public override IEnumerable<(string ID, string Name, string Code)> GetAssetsToGenerate() + { + yield return (ProjectName, ProjectName, + SourceCode + .PatchTargetFrameworks(TargetFrameworks.All) + .PatchCodeWithReplace("$MSTestVersion$", MSTestVersion)); + } + + private const string SourceCode = """ +#file LeakTests.csproj + + + + Exe + true + $TargetFrameworks$ + preview + + + true + + + + + + + + +#file UnitTest1.cs +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using Microsoft.VisualStudio.TestTools.UnitTesting; + + +[TestClass] +public class TestClass +{ + private static ConcurrentBag> _testContexts = new(); + + [AssemblyInitialize] + public static void AssemblyInitialize(TestContext context) + => _testContexts.Add(new WeakReference(context)); + + [ClassInitialize] + public static void ClassInitialize(TestContext context) + => _testContexts.Add(new WeakReference(context)); + + public TestContext TestContext { get; set; } + + [TestMethod] + [DynamicData(nameof(Data))] + public void Test3(int a) + => _testContexts.Add(new WeakReference(TestContext)); + + [ClassCleanup] + public static void ClassCleanup(TestContext testContext) + => _testContexts.Add(new WeakReference(testContext)); + + [AssemblyCleanup] + public static void AssemblyCleanup() + { + for (int i = 0; i < 3; i++) + { + GC.Collect(); + GC.WaitForPendingFinalizers(); + } + + // Assembly init, class init, 100 tests, and class cleanup. (total 103). + Assert.AreEqual(103, _testContexts.Count); + + var alive = 0; + foreach (var weakReference in _testContexts) + { + if (weakReference.TryGetTarget(out _)) + { + alive++; + } + } + + // AssemblyCleanup is executed along with the last test. + // So, we are still holding 2 references to the TestContext. The one for the execution of last test, as well as the one for ClassCleanup. + // Holding into these two references is okay. + Assert.AreEqual(2, alive); + } + + public static IEnumerable Data + { + get + { + for (int i = 0; i < 100; i++) + { + yield return i; + } + } + } +} + +"""; + } + + public TestContext TestContext { get; set; } +} diff --git a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/LifecycleTests.cs b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/LifecycleTests.cs new file mode 100644 index 0000000000..3e233f13e3 --- /dev/null +++ b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/LifecycleTests.cs @@ -0,0 +1,251 @@ +// 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; + +[TestClass] +public sealed class LifecycleTests : AcceptanceTestBase +{ + [TestMethod] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] + public async Task LifecycleTest(string tfm) + { + var testHost = TestHost.LocateFrom(AssetFixture.ProjectPath, TestAssetFixture.ProjectName, tfm); + TestHostResult testHostResult = await testHost.ExecuteAsync("--settings my.runsettings", cancellationToken: TestContext.CancellationToken); + + testHostResult.AssertExitCodeIs(ExitCodes.Success); + testHostResult.AssertOutputContainsSummary(failed: 0, passed: 4, skipped: 0); + // Order is: + // - Assembly initialize + // - foreach test class + // - ClassInitialize + // - foreach test: + // - GlobalTestInitialize + // - ctor + // - TestContext property setter + // - TestInitialize + // - TestMethod + // - TestCleanup + // - Dispose + // - GlobalTestCleanup + // - ClassCleanup + // - AssemblyCleanup + testHostResult.AssertOutputContains(""" + AssemblyInitialize called. + TestClass1.ClassInitialize called. + GlobalTestInitialize called for 'TestMethodParameterized (0)'. + TestClass1 constructor called. + TestContext property set for TestClass1. + TestClass1.TestInitialize for 'TestMethodParameterized (0)' is called. + TestMethodParameterized called with: 0 + TestClass1.TestCleanup for 'TestMethodParameterized (0)' is called. + TestClass1 disposed. + GlobalTestCleanup called for 'TestMethodParameterized (0)'. + GlobalTestInitialize called for 'TestMethodParameterized (1)'. + TestClass1 constructor called. + TestContext property set for TestClass1. + TestClass1.TestInitialize for 'TestMethodParameterized (1)' is called. + TestMethodParameterized called with: 1 + TestClass1.TestCleanup for 'TestMethodParameterized (1)' is called. + TestClass1 disposed. + GlobalTestCleanup called for 'TestMethodParameterized (1)'. + GlobalTestInitialize called for 'TestMethodNonParameterized'. + TestClass1 constructor called. + TestContext property set for TestClass1. + TestClass1.TestInitialize for 'TestMethodNonParameterized' is called. + TestMethodNonParameterized called + TestClass1.TestCleanup for 'TestMethodNonParameterized' is called. + TestClass1 disposed. + GlobalTestCleanup called for 'TestMethodNonParameterized'. + TestClass1.ClassCleanup called. + TestClass2.ClassInitialize called. + GlobalTestInitialize called for 'TestMethodFromTestClass2'. + TestClass2 constructor called. + TestContext property set for TestClass2. + TestClass2.TestInitialize for 'TestMethodFromTestClass2' is called. + TestMethodFromTestClass2 called + TestClass2.TestCleanup for 'TestMethodFromTestClass2' is called. + TestClass2 disposed. + GlobalTestCleanup called for 'TestMethodFromTestClass2'. + TestClass2.ClassCleanup called. + AssemblyCleanup called. + """); + } + + public sealed class TestAssetFixture() : TestAssetFixtureBase(AcceptanceFixture.NuGetGlobalPackagesFolder) + { + public const string ProjectName = "LifecycleTests"; + + public string ProjectPath => GetAssetPath(ProjectName); + + public override IEnumerable<(string ID, string Name, string Code)> GetAssetsToGenerate() + { + yield return (ProjectName, ProjectName, + SourceCode + .PatchTargetFrameworks(TargetFrameworks.All) + .PatchCodeWithReplace("$MSTestVersion$", MSTestVersion)); + } + + private const string SourceCode = """ +#file LifecycleTests.csproj + + + + Exe + true + $TargetFrameworks$ + preview + + + + + + + + + + PreserveNewest + + + + +#file TestClass1.cs +using System; +using System.Collections.Generic; +using System.Text; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +[TestClass] +public static class Fixtures +{ + [AssemblyInitialize] + public static void AssemblyInitialize(TestContext context) + => Console.WriteLine("AssemblyInitialize called."); + + [AssemblyCleanup] + public static void AssemblyCleanup(TestContext context) + => Console.WriteLine("AssemblyCleanup called."); + + [GlobalTestInitialize] + public static void GlobalTestInitialize(TestContext context) + => Console.WriteLine($"GlobalTestInitialize called for '{context.TestDisplayName}'."); + + [GlobalTestCleanup] + public static void GlobalTestCleanup(TestContext context) + => Console.WriteLine($"GlobalTestCleanup called for '{context.TestDisplayName}'."); +} + +[TestClass] +public class TestClass1 : IDisposable +{ + public TestClass1() + { + Console.WriteLine("TestClass1 constructor called."); + } + + public TestContext TestContext + { + get => field; + set + { + field = value; + Console.WriteLine("TestContext property set for TestClass1."); + } + } + + [TestInitialize] + public void TestInitialize() + { + Console.WriteLine($"TestClass1.TestInitialize for '{TestContext.TestDisplayName}' is called."); + } + + [TestCleanup] + public void TestCleanup() + { + Console.WriteLine($"TestClass1.TestCleanup for '{TestContext.TestDisplayName}' is called."); + } + + [ClassInitialize] + public static void ClassInitialize(TestContext context) + => Console.WriteLine("TestClass1.ClassInitialize called."); + + [ClassCleanup] + public static void ClassCleanup(TestContext context) + => Console.WriteLine("TestClass1.ClassCleanup called."); + + [TestMethod] + [DataRow(0)] + [DataRow(1)] + public void TestMethodParameterized(int a) + => Console.WriteLine($"TestMethodParameterized called with: {a}"); + + [TestMethod] + public void TestMethodNonParameterized() + => Console.WriteLine("TestMethodNonParameterized called"); + + public void Dispose() + => Console.WriteLine("TestClass1 disposed."); +} + +[TestClass] +public class TestClass2 : IDisposable +{ + public TestClass2() + { + Console.WriteLine("TestClass2 constructor called."); + } + + public TestContext TestContext + { + get => field; + set + { + field = value; + Console.WriteLine("TestContext property set for TestClass2."); + } + } + + [TestInitialize] + public void TestInitialize() + { + Console.WriteLine($"TestClass2.TestInitialize for '{TestContext.TestDisplayName}' is called."); + } + + [TestCleanup] + public void TestCleanup() + { + Console.WriteLine($"TestClass2.TestCleanup for '{TestContext.TestDisplayName}' is called."); + } + + [ClassInitialize] + public static void ClassInitialize(TestContext context) + => Console.WriteLine("TestClass2.ClassInitialize called."); + + [ClassCleanup] + public static void ClassCleanup(TestContext context) + => Console.WriteLine("TestClass2.ClassCleanup called."); + + [TestMethod] + public void TestMethodFromTestClass2() + => Console.WriteLine("TestMethodFromTestClass2 called"); + + public void Dispose() + => Console.WriteLine("TestClass2 disposed."); +} + +#file my.runsettings + + + false + EndOfClass + + +"""; + } + + public TestContext TestContext { get; set; } +} diff --git a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/MSBuildRunnerTests.cs b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/MSBuildRunnerTests.cs index e826266a0b..61b0aa0643 100644 --- a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/MSBuildRunnerTests.cs +++ b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/MSBuildRunnerTests.cs @@ -28,7 +28,7 @@ public class MSBuildRunnerTests : AcceptanceTestBase } [TestMethod] - [DynamicData(nameof(GetBuildMatrix), DynamicDataSourceType.Method)] + [DynamicData(nameof(GetBuildMatrix))] public async Task MSBuildTestTarget_SingleAndMultiTfm_Should_Run_Solution_Tests(string singleTfmOrMultiTfm, BuildConfiguration buildConfiguration, bool isMultiTfm, string command) { // Get the template project @@ -64,10 +64,10 @@ public async Task MSBuildTestTarget_SingleAndMultiTfm_Should_Run_Solution_Tests( } // Build the solution - DotnetMuxerResult restoreResult = await DotnetCli.RunAsync($"restore -m:1 -nodeReuse:false {solution.SolutionFile} --configfile {nugetFile}", AcceptanceFixture.NuGetGlobalPackagesFolder.Path); + DotnetMuxerResult restoreResult = await DotnetCli.RunAsync($"restore -m:1 -nodeReuse:false {solution.SolutionFile} --configfile {nugetFile}", AcceptanceFixture.NuGetGlobalPackagesFolder.Path, cancellationToken: TestContext.CancellationToken); restoreResult.AssertOutputDoesNotContain("An approximate best match of"); - DotnetMuxerResult testResult = await DotnetCli.RunAsync($"{command} -m:1 -nodeReuse:false {solution.SolutionFile}", AcceptanceFixture.NuGetGlobalPackagesFolder.Path, workingDirectory: generator.TargetAssetPath); + DotnetMuxerResult testResult = await DotnetCli.RunAsync($"{command} -m:1 -nodeReuse:false {solution.SolutionFile}", AcceptanceFixture.NuGetGlobalPackagesFolder.Path, workingDirectory: generator.TargetAssetPath, cancellationToken: TestContext.CancellationToken); if (isMultiTfm) { @@ -85,4 +85,6 @@ public async Task MSBuildTestTarget_SingleAndMultiTfm_Should_Run_Solution_Tests( testResult.AssertOutputMatchesRegex($@"Tests succeeded: '.*TestProject2\..*' \[{singleTfmOrMultiTfm}\|x64\]"); } } + + public TestContext TestContext { get; set; } } diff --git a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/MaxFailedTestsExtensionTests.cs b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/MaxFailedTestsExtensionTests.cs index 6d11054c28..49ddd4443c 100644 --- a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/MaxFailedTestsExtensionTests.cs +++ b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/MaxFailedTestsExtensionTests.cs @@ -18,7 +18,7 @@ public async Task SimpleMaxFailedTestsScenario(string tfm) { var testHost = TestHost.LocateFrom(AssetFixture.TargetAssetPath, AssetName, tfm); - TestHostResult testHostResult = await testHost.ExecuteAsync("--maximum-failed-tests 3"); + TestHostResult testHostResult = await testHost.ExecuteAsync("--maximum-failed-tests 3", cancellationToken: TestContext.CancellationToken); testHostResult.AssertExitCodeIs(ExitCodes.TestExecutionStoppedForMaxFailedTests); int total = int.Parse(Regex.Match(testHostResult.StandardOutput, @"total: (\d+)").Groups[1].Value, CultureInfo.InvariantCulture); @@ -26,10 +26,10 @@ public async Task SimpleMaxFailedTestsScenario(string tfm) // We can't know the number of tests that will be executed exactly due to the async // nature of publish/consume on the platform side. But we expect the cancellation to // happen "fast" enough that we don't execute all tests. - Assert.IsTrue(total < 12); - Assert.IsTrue(total >= 5); + Assert.IsLessThan(12, total); + Assert.IsGreaterThanOrEqualTo(5, total); - testHostResult = await testHost.ExecuteAsync(); + testHostResult = await testHost.ExecuteAsync(cancellationToken: TestContext.CancellationToken); testHostResult.AssertExitCodeIs(ExitCodes.AtLeastOneTestFailed); total = int.Parse(Regex.Match(testHostResult.StandardOutput, @"total: (\d+)").Groups[1].Value, CultureInfo.InvariantCulture); @@ -144,4 +144,6 @@ public async Task Test12() .PatchCodeWithReplace("$MSTestVersion$", MSTestVersion)); } } + + public TestContext TestContext { get; set; } } diff --git a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/NativeAotTests.cs b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/NativeAotTests.cs index b404a0039e..c867b34df3 100644 --- a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/NativeAotTests.cs +++ b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/NativeAotTests.cs @@ -108,19 +108,22 @@ await RetryHelper.RetryAsync( await DotnetCli.RunAsync( $"restore -m:1 -nodeReuse:false {generator.TargetAssetPath} -r {RID}", AcceptanceFixture.NuGetGlobalPackagesFolder.Path, - retryCount: 0); + retryCount: 0, + cancellationToken: TestContext.CancellationToken); DotnetMuxerResult compilationResult = await DotnetCli.RunAsync( $"publish -m:1 -nodeReuse:false {generator.TargetAssetPath} -r {RID}", AcceptanceFixture.NuGetGlobalPackagesFolder.Path, - timeoutInSeconds: 90, - retryCount: 0); + retryCount: 0, + cancellationToken: TestContext.CancellationToken); compilationResult.AssertOutputContains("Generating native code"); var testHost = TestHost.LocateFrom(generator.TargetAssetPath, "NativeAotTests", TargetFrameworks.NetCurrent, RID, Verb.publish); - TestHostResult result = await testHost.ExecuteAsync(); + TestHostResult result = await testHost.ExecuteAsync(cancellationToken: TestContext.CancellationToken); result.AssertOutputContains($"MSTest.Engine v{MSTestEngineVersion}"); result.AssertExitCodeIs(0); }, times: 15, every: TimeSpan.FromSeconds(5)); } + + public TestContext TestContext { get; set; } } diff --git a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/OutputTests.cs b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/OutputTests.cs index a52aaf0899..0a2f3d678e 100644 --- a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/OutputTests.cs +++ b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/OutputTests.cs @@ -14,7 +14,7 @@ public sealed class OutputTests : AcceptanceTestBase. Actual:<2>."); @@ -80,4 +80,6 @@ public void TestMethod() } """; } + + public TestContext TestContext { get; set; } } diff --git a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/ParameterizedTestTests.cs b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/ParameterizedTestTests.cs index 0a029a6421..92e1240e2f 100644 --- a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/ParameterizedTestTests.cs +++ b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/ParameterizedTestTests.cs @@ -12,6 +12,7 @@ public class ParameterizedTestTests : AcceptanceTestBase await UsingDataRowThatDoesNotRoundTripUsingDataContractJsonSerializerCore(currentTfm, "AppDomainEnabled"); + + [TestMethod] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] + public async Task UsingDataRowThatDoesNotRoundTripUsingDataContractJsonSerializerWithoutAppDomains(string currentTfm) + => await UsingDataRowThatDoesNotRoundTripUsingDataContractJsonSerializerCore(currentTfm, "AppDomainDisabled"); + + private static async Task UsingDataRowThatDoesNotRoundTripUsingDataContractJsonSerializerCore(string currentTfm, string runSettings) + { + var testHost = TestHost.LocateFrom(AssetFixture.GetAssetPath(DataRowAssetName), DataRowAssetName, currentTfm); + + TestHostResult testHostResult = await testHost.ExecuteAsync($"--settings {runSettings}.runsettings --filter ClassName=ParameterizedTestSerializationIssue2390"); + + testHostResult.AssertExitCodeIs(ExitCodes.Success); + testHostResult.AssertOutputContainsSummary(failed: 0, passed: 3, skipped: 0); + } + private static async Task RunTestsAsync(string currentTfm, string assetName, bool? isEmptyDataInconclusive) { var testHost = TestHost.LocateFrom(AssetFixture.GetAssetPath(assetName), assetName, currentTfm); @@ -163,8 +184,98 @@ public sealed class TestAssetFixture() : TestAssetFixtureBase(AcceptanceFixture. SourceCodeDataSource .PatchTargetFrameworks(TargetFrameworks.All) .PatchCodeWithReplace("$MSTestVersion$", MSTestVersion)); + yield return (DataRowAssetName, DataRowAssetName, + SourceCodeDataRow + .PatchTargetFrameworks(TargetFrameworks.All) + .PatchCodeWithReplace("$MSTestVersion$", MSTestVersion)); } + private const string SourceCodeDataRow = """ +#file DataRowTests.csproj + + + + Exe + true + $TargetFrameworks$ + preview + + + + + + + + + + PreserveNewest + + + + + + PreserveNewest + + + + + +#file AppDomainEnabled.runsettings + + + + + false + + + +#file AppDomainDisabled.runsettings + + + + true + + + +#file UnitTest1.cs + +using System; +using System.Collections.Generic; +using System.Linq; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +// Test for https://github.com/microsoft/testfx/issues/2390 +[TestClass] +public class ParameterizedTestSerializationIssue2390 +{ + [TestMethod] + [DataRow((byte)0, new object[] { (byte)0 })] + [DataRow((short)0, new object[] { (short)0 })] + [DataRow(0L, new object[] { 0L })] + public void CheckNestedInputTypes(object expected, object nested) + { + object[] array = (object[])nested; + object actual = Assert.ContainsSingle(array); + +#if NETFRAMEWORK + var appDomainEnabled = Environment.GetCommandLineArgs().Contains("AppDomainEnabled.runsettings"); + if (appDomainEnabled) + { + // Buggy behavior, because of app domains. + Assert.AreEqual(typeof(int), actual.GetType(), AppDomain.CurrentDomain.FriendlyName); + } + else +#endif + { + Assert.AreEqual(expected.GetType(), actual.GetType(), AppDomain.CurrentDomain.FriendlyName); + Assert.AreEqual(expected, actual); + } + } +} + +"""; + private const string SourceCodeDynamicData = """ #file DynamicData.csproj @@ -181,7 +292,7 @@ public sealed class TestAssetFixture() : TestAssetFixtureBase(AcceptanceFixture. - + PreserveNewest @@ -349,4 +460,6 @@ public class CustomEmptyTestDataSourceAttribute : Attribute, ITestDataSource } """; } + + public TestContext TestContext { get; set; } } diff --git a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/PublishAotNonNativeTests.cs b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/PublishAotNonNativeTests.cs index e2bdeecdad..0ab9b4ec83 100644 --- a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/PublishAotNonNativeTests.cs +++ b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/PublishAotNonNativeTests.cs @@ -24,7 +24,7 @@ public async Task RunTests_ThatEnablePublishAOT_ButDontBuildToNative() .PatchCodeWithReplace("$TargetFramework$", $"{TargetFrameworks.NetCurrent}") .PatchCodeWithReplace("$MSTestVersion$", MSTestVersion)); - DotnetMuxerResult compilationResult = await DotnetCli.RunAsync($"test -c Debug {generator.TargetAssetPath}", AcceptanceFixture.NuGetGlobalPackagesFolder.Path, workingDirectory: generator.TargetAssetPath, failIfReturnValueIsNotZero: false); + DotnetMuxerResult compilationResult = await DotnetCli.RunAsync($"test -c Debug {generator.TargetAssetPath}", AcceptanceFixture.NuGetGlobalPackagesFolder.Path, workingDirectory: generator.TargetAssetPath, failIfReturnValueIsNotZero: false, cancellationToken: TestContext.CancellationToken); // In the real-world issue, access to path C:\Program Files\dotnet\ is denied, but we run this from a local .dotnet folder, where we have write access. // So instead of relying on the test run failing because of AccessDenied, we check the output, and see where TestResults were placed. @@ -68,9 +68,12 @@ we end up with a -dev or -ci version which will lose resolution over -preview de -#file dotnet.config -[dotnet.test.runner] -name= "VSTest" +#file global.json +{ + "test": { + "runner": "VSTest" + } +} #file UnitTest1.cs @@ -89,4 +92,6 @@ public void TestMethod1() } } """; + + public TestContext TestContext { get; set; } } diff --git a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/RetryTests.cs b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/RetryTests.cs index 47172caaa4..54405e8e69 100644 --- a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/RetryTests.cs +++ b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/RetryTests.cs @@ -14,7 +14,7 @@ public sealed class RetryTests : AcceptanceTestBase public async Task BasicRetryScenarioTest() { var testHost = TestHost.LocateFrom(AssetFixture.ProjectPath, TestAssetFixture.ProjectName, TargetFrameworks.NetCurrent); - TestHostResult testHostResult = await testHost.ExecuteAsync("--settings my.runsettings"); + TestHostResult testHostResult = await testHost.ExecuteAsync("--settings my.runsettings", cancellationToken: TestContext.CancellationToken); testHostResult.AssertExitCodeIs(ExitCodes.AtLeastOneTestFailed); testHostResult.AssertOutputContains(""" @@ -145,4 +145,6 @@ public static void ClassCleanup() """; } + + public TestContext TestContext { get; set; } } diff --git a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/RunnerTests.cs b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/RunnerTests.cs index e8316ee811..83035ada19 100644 --- a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/RunnerTests.cs +++ b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/RunnerTests.cs @@ -15,7 +15,7 @@ public class RunnerTests : AcceptanceTestBase private const string AssetName = "MSTestProject"; [TestMethod] - [DynamicData(nameof(GetBuildMatrixTfmBuildVerbConfiguration), typeof(AcceptanceTestBase), DynamicDataSourceType.Method)] + [DynamicData(nameof(GetBuildMatrixTfmBuildVerbConfiguration), typeof(AcceptanceTestBase))] public async SystemTask EnableMSTestRunner_True_Will_Run_Standalone(string tfm, BuildConfiguration buildConfiguration, Verb verb) { using TestAsset generator = await TestAsset.GenerateAssetAsync( @@ -29,22 +29,22 @@ public async SystemTask EnableMSTestRunner_True_Will_Run_Standalone(string tfm, .PatchCodeWithReplace("$Extra$", string.Empty)); DotnetMuxerResult compilationResult = await DotnetCli.RunAsync( $"restore -m:1 -nodeReuse:false {generator.TargetAssetPath} -r {RID}", - AcceptanceFixture.NuGetGlobalPackagesFolder.Path); + AcceptanceFixture.NuGetGlobalPackagesFolder.Path, cancellationToken: TestContext.CancellationToken); compilationResult = await DotnetCli.RunAsync( $"{verb} -m:1 -nodeReuse:false {generator.TargetAssetPath} -c {buildConfiguration} -r {RID}", - AcceptanceFixture.NuGetGlobalPackagesFolder.Path); + AcceptanceFixture.NuGetGlobalPackagesFolder.Path, cancellationToken: TestContext.CancellationToken); Build binLog = Serialization.Read(compilationResult.BinlogPath); Assert.AreNotEqual(0, binLog.FindChildrenRecursive() .Count(x => x.Title.Contains("ProjectCapability") && x.Children.Any(c => ((Item)c).Name == "TestingPlatformServer"))); var testHost = TestHost.LocateFrom(generator.TargetAssetPath, AssetName, tfm, buildConfiguration: buildConfiguration, verb: verb); - TestHostResult testHostResult = await testHost.ExecuteAsync(); + TestHostResult testHostResult = await testHost.ExecuteAsync(cancellationToken: TestContext.CancellationToken); testHostResult.AssertOutputContainsSummary(failed: 0, passed: 1, skipped: 0); } [TestMethod] - [DynamicData(nameof(GetBuildMatrixTfmBuildVerbConfiguration), typeof(AcceptanceTestBase), DynamicDataSourceType.Method)] + [DynamicData(nameof(GetBuildMatrixTfmBuildVerbConfiguration), typeof(AcceptanceTestBase))] public async SystemTask EnableMSTestRunner_True_WithCustomEntryPoint_Will_Run_Standalone(string tfm, BuildConfiguration buildConfiguration, Verb verb) { using TestAsset generator = await TestAsset.GenerateAssetAsync( @@ -68,17 +68,17 @@ public async SystemTask EnableMSTestRunner_True_WithCustomEntryPoint_Will_Run_St False preview """)); - await DotnetCli.RunAsync($"restore -m:1 -nodeReuse:false {generator.TargetAssetPath} -r {RID}", AcceptanceFixture.NuGetGlobalPackagesFolder.Path); + await DotnetCli.RunAsync($"restore -m:1 -nodeReuse:false {generator.TargetAssetPath} -r {RID}", AcceptanceFixture.NuGetGlobalPackagesFolder.Path, cancellationToken: TestContext.CancellationToken); await DotnetCli.RunAsync( $"{verb} -m:1 -nodeReuse:false {generator.TargetAssetPath} -c {buildConfiguration} -r {RID}", - AcceptanceFixture.NuGetGlobalPackagesFolder.Path); + AcceptanceFixture.NuGetGlobalPackagesFolder.Path, cancellationToken: TestContext.CancellationToken); var testHost = TestHost.LocateFrom(generator.TargetAssetPath, AssetName, tfm, buildConfiguration: buildConfiguration, verb: verb); - TestHostResult testHostResult = await testHost.ExecuteAsync(); + TestHostResult testHostResult = await testHost.ExecuteAsync(cancellationToken: TestContext.CancellationToken); testHostResult.AssertOutputContainsSummary(failed: 0, passed: 1, skipped: 0); } [TestMethod] - [DynamicData(nameof(GetBuildMatrixTfmBuildVerbConfiguration), typeof(AcceptanceTestBase), DynamicDataSourceType.Method)] + [DynamicData(nameof(GetBuildMatrixTfmBuildVerbConfiguration), typeof(AcceptanceTestBase))] 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( @@ -90,26 +90,26 @@ public async SystemTask EnableMSTestRunner_False_Will_Run_Empty_Program_EntryPoi .PatchCodeWithReplace("$EnableMSTestRunner$", "false") .PatchCodeWithReplace("$OutputType$", "Exe") .PatchCodeWithReplace("$Extra$", string.Empty)); - await DotnetCli.RunAsync($"restore -m:1 -nodeReuse:false {generator.TargetAssetPath} -r {RID}", AcceptanceFixture.NuGetGlobalPackagesFolder.Path); + await DotnetCli.RunAsync($"restore -m:1 -nodeReuse:false {generator.TargetAssetPath} -r {RID}", AcceptanceFixture.NuGetGlobalPackagesFolder.Path, cancellationToken: TestContext.CancellationToken); try { - await DotnetCli.RunAsync($"{verb} -m:1 -nodeReuse:false {generator.TargetAssetPath} -c {buildConfiguration} -r {RID}", AcceptanceFixture.NuGetGlobalPackagesFolder.Path); + await DotnetCli.RunAsync($"{verb} -m:1 -nodeReuse:false {generator.TargetAssetPath} -c {buildConfiguration} -r {RID}", AcceptanceFixture.NuGetGlobalPackagesFolder.Path, cancellationToken: TestContext.CancellationToken); var testHost = TestHost.LocateFrom(generator.TargetAssetPath, AssetName, tfm, buildConfiguration: buildConfiguration, verb: verb); - TestHostResult testHostResult = await testHost.ExecuteAsync(); + TestHostResult testHostResult = await testHost.ExecuteAsync(cancellationToken: TestContext.CancellationToken); Assert.AreEqual(string.Empty, testHostResult.StandardOutput); } catch (Exception ex) { if (TargetFrameworks.NetFramework.Any(x => x == tfm)) { - Assert.IsTrue(ex.Message.Contains("Program does not contain a static 'Main' method suitable for an entry point"), ex.Message); + Assert.Contains("Program does not contain a static 'Main' method suitable for an entry point", ex.Message, ex.Message); // .NET Framework does not insert the entry point for empty program. } } } [TestMethod] - [DynamicData(nameof(GetBuildMatrixTfmBuildVerbConfiguration), typeof(AcceptanceTestBase), DynamicDataSourceType.Method)] + [DynamicData(nameof(GetBuildMatrixTfmBuildVerbConfiguration), typeof(AcceptanceTestBase))] public async SystemTask EnableMSTestRunner_False_Wont_Flow_TestingPlatformServer_Capability(string tfm, BuildConfiguration buildConfiguration, Verb verb) { using TestAsset generator = await TestAsset.GenerateAssetAsync( @@ -122,11 +122,13 @@ public async SystemTask EnableMSTestRunner_False_Wont_Flow_TestingPlatformServer .PatchCodeWithReplace("$OutputType$", string.Empty) .PatchCodeWithReplace("$Extra$", string.Empty)); - await DotnetCli.RunAsync($"restore -m:1 -nodeReuse:false {generator.TargetAssetPath} -r {RID}", AcceptanceFixture.NuGetGlobalPackagesFolder.Path); - DotnetMuxerResult result = await DotnetCli.RunAsync($"{verb} -m:1 -nodeReuse:false {generator.TargetAssetPath} -c {buildConfiguration} -r {RID} ", AcceptanceFixture.NuGetGlobalPackagesFolder.Path); + await DotnetCli.RunAsync($"restore -m:1 -nodeReuse:false {generator.TargetAssetPath} -r {RID}", AcceptanceFixture.NuGetGlobalPackagesFolder.Path, cancellationToken: TestContext.CancellationToken); + DotnetMuxerResult result = await DotnetCli.RunAsync($"{verb} -m:1 -nodeReuse:false {generator.TargetAssetPath} -c {buildConfiguration} -r {RID} ", AcceptanceFixture.NuGetGlobalPackagesFolder.Path, cancellationToken: TestContext.CancellationToken); Build binLog = Serialization.Read(result.BinlogPath); Assert.IsFalse(binLog.FindChildrenRecursive() .Any(x => x.Title.Contains("ProjectCapability") && x.Children.Any(c => ((Item)c).Name == "TestingPlatformServer"))); } + + public TestContext TestContext { get; set; } } diff --git a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/RunsettingsTests.cs b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/RunsettingsTests.cs index 9944f0d74f..2d484914f0 100644 --- a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/RunsettingsTests.cs +++ b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/RunsettingsTests.cs @@ -36,7 +36,7 @@ public sealed class RunSettingsTests : AcceptanceTestBase -#file dotnet.config -[dotnet.test.runner] -name= "VSTest" +#file global.json +{ + "test": { + "runner": "VSTest" + } +} #file UnitTest1.cs using Microsoft.VisualStudio.TestTools.UnitTesting; @@ -54,7 +57,7 @@ public void TestMethod1() """; [TestMethod] - [DynamicData(nameof(GetBuildMatrixMultiTfmFoldedBuildConfiguration), typeof(AcceptanceTestBase), DynamicDataSourceType.Method)] + [DynamicData(nameof(GetBuildMatrixMultiTfmFoldedBuildConfiguration), typeof(AcceptanceTestBase))] public async Task RunTests_With_VSTest(string multiTfm, BuildConfiguration buildConfiguration) { using TestAsset testAsset = await TestAsset.GenerateAssetAsync( @@ -64,8 +67,8 @@ public async Task RunTests_With_VSTest(string multiTfm, BuildConfiguration build .PatchCodeWithReplace("$TargetFramework$", multiTfm) .PatchCodeWithReplace("$ExtraProperties$", "true")); - DotnetMuxerResult compilationResult = await DotnetCli.RunAsync($"test -c {buildConfiguration} {testAsset.TargetAssetPath}", AcceptanceFixture.NuGetGlobalPackagesFolder.Path, workingDirectory: testAsset.TargetAssetPath); - Assert.AreEqual(0, compilationResult.ExitCode); + DotnetMuxerResult compilationResult = await DotnetCli.RunAsync($"test -c {buildConfiguration} {testAsset.TargetAssetPath}", AcceptanceFixture.NuGetGlobalPackagesFolder.Path, workingDirectory: testAsset.TargetAssetPath, cancellationToken: TestContext.CancellationToken); + compilationResult.AssertExitCodeIs(0); compilationResult.AssertOutputMatchesRegex(@"Passed! - Failed: 0, Passed: 1, Skipped: 0, Total: 1, Duration: .* [m]?s - MSTestSdk.dll \(net9\.0\)"); #if !SKIP_INTERMEDIATE_TARGET_FRAMEWORKS @@ -81,7 +84,7 @@ public async Task RunTests_With_VSTest(string multiTfm, BuildConfiguration build } [TestMethod] - [DynamicData(nameof(GetBuildMatrixMultiTfmFoldedBuildConfiguration), typeof(AcceptanceTestBase), DynamicDataSourceType.Method)] + [DynamicData(nameof(GetBuildMatrixMultiTfmFoldedBuildConfiguration), typeof(AcceptanceTestBase))] public async Task RunTests_With_MSTestRunner_DotnetTest(string multiTfm, BuildConfiguration buildConfiguration) { using TestAsset testAsset = await TestAsset.GenerateAssetAsync( @@ -91,8 +94,8 @@ public async Task RunTests_With_MSTestRunner_DotnetTest(string multiTfm, BuildCo .PatchCodeWithReplace("$TargetFramework$", multiTfm) .PatchCodeWithReplace("$ExtraProperties$", string.Empty)); - DotnetMuxerResult compilationResult = await DotnetCli.RunAsync($"test -c {buildConfiguration} {testAsset.TargetAssetPath}", AcceptanceFixture.NuGetGlobalPackagesFolder.Path, workingDirectory: testAsset.TargetAssetPath); - Assert.AreEqual(0, compilationResult.ExitCode); + DotnetMuxerResult compilationResult = await DotnetCli.RunAsync($"test -c {buildConfiguration} {testAsset.TargetAssetPath}", AcceptanceFixture.NuGetGlobalPackagesFolder.Path, workingDirectory: testAsset.TargetAssetPath, cancellationToken: TestContext.CancellationToken); + compilationResult.AssertExitCodeIs(0); compilationResult.AssertOutputMatchesRegex(@"Tests succeeded: .* \[net9\.0|x64\]"); #if !SKIP_INTERMEDIATE_TARGET_FRAMEWORKS @@ -108,7 +111,7 @@ public async Task RunTests_With_MSTestRunner_DotnetTest(string multiTfm, BuildCo } [TestMethod] - [DynamicData(nameof(GetBuildMatrixMultiTfmFoldedBuildConfiguration), typeof(AcceptanceTestBase), DynamicDataSourceType.Method)] + [DynamicData(nameof(GetBuildMatrixMultiTfmFoldedBuildConfiguration), typeof(AcceptanceTestBase))] public async Task RunTests_With_MSTestRunner_Standalone(string multiTfm, BuildConfiguration buildConfiguration) { using TestAsset testAsset = await TestAsset.GenerateAssetAsync( @@ -118,18 +121,18 @@ public async Task RunTests_With_MSTestRunner_Standalone(string multiTfm, BuildCo .PatchCodeWithReplace("$TargetFramework$", multiTfm) .PatchCodeWithReplace("$ExtraProperties$", string.Empty)); - DotnetMuxerResult compilationResult = await DotnetCli.RunAsync($"build -c {buildConfiguration} {testAsset.TargetAssetPath}", AcceptanceFixture.NuGetGlobalPackagesFolder.Path); - Assert.AreEqual(0, compilationResult.ExitCode); + DotnetMuxerResult compilationResult = await DotnetCli.RunAsync($"build -c {buildConfiguration} {testAsset.TargetAssetPath}", AcceptanceFixture.NuGetGlobalPackagesFolder.Path, cancellationToken: TestContext.CancellationToken); + compilationResult.AssertExitCodeIs(0); foreach (string tfm in multiTfm.Split(";")) { var testHost = TestHost.LocateFrom(testAsset.TargetAssetPath, AssetName, tfm, buildConfiguration: buildConfiguration); - TestHostResult testHostResult = await testHost.ExecuteAsync(); + TestHostResult testHostResult = await testHost.ExecuteAsync(cancellationToken: TestContext.CancellationToken); testHostResult.AssertOutputContainsSummary(0, 1, 0); } } [TestMethod] - [DynamicData(nameof(GetBuildMatrixMultiTfmFoldedBuildConfiguration), typeof(AcceptanceTestBase), DynamicDataSourceType.Method)] + [DynamicData(nameof(GetBuildMatrixMultiTfmFoldedBuildConfiguration), typeof(AcceptanceTestBase))] public async Task RunTests_With_CentralPackageManagement_Standalone(string multiTfm, BuildConfiguration buildConfiguration) { using TestAsset testAsset = await TestAsset.GenerateAssetAsync( @@ -139,12 +142,12 @@ public async Task RunTests_With_CentralPackageManagement_Standalone(string multi .PatchCodeWithReplace("$TargetFramework$", multiTfm) .PatchCodeWithReplace("$ExtraProperties$", string.Empty)); - DotnetMuxerResult compilationResult = await DotnetCli.RunAsync($"build -c {buildConfiguration} {testAsset.TargetAssetPath}", AcceptanceFixture.NuGetGlobalPackagesFolder.Path); - Assert.AreEqual(0, compilationResult.ExitCode); + DotnetMuxerResult compilationResult = await DotnetCli.RunAsync($"build -c {buildConfiguration} {testAsset.TargetAssetPath}", AcceptanceFixture.NuGetGlobalPackagesFolder.Path, cancellationToken: TestContext.CancellationToken); + compilationResult.AssertExitCodeIs(0); foreach (string tfm in multiTfm.Split(";")) { var testHost = TestHost.LocateFrom(testAsset.TargetAssetPath, AssetName, tfm, buildConfiguration: buildConfiguration); - TestHostResult testHostResult = await testHost.ExecuteAsync(); + TestHostResult testHostResult = await testHost.ExecuteAsync(cancellationToken: TestContext.CancellationToken); testHostResult.AssertOutputContainsSummary(0, 1, 0); } } @@ -177,11 +180,16 @@ public async Task RunTests_With_CentralPackageManagement_Standalone(string multi "true", "--hangdump", "--crashdump"); + + yield return new(buildConfig.MultiTfm, buildConfig.BuildConfiguration, + "true", + "--report-azdo", + "--crashdump"); } } [TestMethod] - [DynamicData(nameof(RunTests_With_MSTestRunner_Standalone_Plus_Extensions_Data), DynamicDataSourceType.Method)] + [DynamicData(nameof(RunTests_With_MSTestRunner_Standalone_Plus_Extensions_Data))] public async Task RunTests_With_MSTestRunner_Standalone_Selectively_Enabled_Extensions(string multiTfm, BuildConfiguration buildConfiguration, string msbuildExtensionEnableFragment, string enableCommandLineArg, @@ -194,21 +202,21 @@ public async Task RunTests_With_MSTestRunner_Standalone_Selectively_Enabled_Exte .PatchCodeWithReplace("$TargetFramework$", multiTfm) .PatchCodeWithReplace("$ExtraProperties$", msbuildExtensionEnableFragment)); - DotnetMuxerResult compilationResult = await DotnetCli.RunAsync($"build -c {buildConfiguration} {testAsset.TargetAssetPath}", AcceptanceFixture.NuGetGlobalPackagesFolder.Path); - Assert.AreEqual(0, compilationResult.ExitCode); + DotnetMuxerResult compilationResult = await DotnetCli.RunAsync($"build -c {buildConfiguration} {testAsset.TargetAssetPath}", AcceptanceFixture.NuGetGlobalPackagesFolder.Path, cancellationToken: TestContext.CancellationToken); + compilationResult.AssertExitCodeIs(0); foreach (string tfm in multiTfm.Split(";")) { var testHost = TestHost.LocateFrom(testAsset.TargetAssetPath, AssetName, tfm, buildConfiguration: buildConfiguration); - TestHostResult testHostResult = await testHost.ExecuteAsync(command: enableCommandLineArg); + TestHostResult testHostResult = await testHost.ExecuteAsync(command: enableCommandLineArg, cancellationToken: TestContext.CancellationToken); testHostResult.AssertOutputContainsSummary(0, 1, 0); - testHostResult = await testHost.ExecuteAsync(command: invalidCommandLineArg); + testHostResult = await testHost.ExecuteAsync(command: invalidCommandLineArg, cancellationToken: TestContext.CancellationToken); Assert.AreEqual(ExitCodes.InvalidCommandLine, testHostResult.ExitCode); } } [TestMethod] - [DynamicData(nameof(GetBuildMatrixMultiTfmFoldedBuildConfiguration), typeof(AcceptanceTestBase), DynamicDataSourceType.Method)] + [DynamicData(nameof(GetBuildMatrixMultiTfmFoldedBuildConfiguration), typeof(AcceptanceTestBase))] public async Task RunTests_With_MSTestRunner_Standalone_EnableAll_Extensions(string multiTfm, BuildConfiguration buildConfiguration) { using TestAsset testAsset = await TestAsset.GenerateAssetAsync( @@ -218,12 +226,12 @@ public async Task RunTests_With_MSTestRunner_Standalone_EnableAll_Extensions(str .PatchCodeWithReplace("$TargetFramework$", multiTfm) .PatchCodeWithReplace("$ExtraProperties$", "AllMicrosoft")); - DotnetMuxerResult compilationResult = await DotnetCli.RunAsync($"build -c {buildConfiguration} {testAsset.TargetAssetPath}", AcceptanceFixture.NuGetGlobalPackagesFolder.Path); - Assert.AreEqual(0, compilationResult.ExitCode); + DotnetMuxerResult compilationResult = await DotnetCli.RunAsync($"build -c {buildConfiguration} {testAsset.TargetAssetPath}", AcceptanceFixture.NuGetGlobalPackagesFolder.Path, cancellationToken: TestContext.CancellationToken); + compilationResult.AssertExitCodeIs(0); foreach (string tfm in multiTfm.Split(";")) { var testHost = TestHost.LocateFrom(testAsset.TargetAssetPath, AssetName, tfm, buildConfiguration: buildConfiguration); - TestHostResult testHostResult = await testHost.ExecuteAsync(command: "--coverage --retry-failed-tests 3 --report-trx --crashdump --hangdump"); + TestHostResult testHostResult = await testHost.ExecuteAsync(command: "--coverage --retry-failed-tests 3 --report-trx --crashdump --hangdump --report-azdo", cancellationToken: TestContext.CancellationToken); testHostResult.AssertOutputContainsSummary(0, 1, 0); } } @@ -238,7 +246,7 @@ public async Task RunTests_With_MSTestRunner_Standalone_EnableAll_Extensions(str } [TestMethod] - [DynamicData(nameof(RunTests_With_MSTestRunner_Standalone_Default_Extensions_Data), DynamicDataSourceType.Method)] + [DynamicData(nameof(RunTests_With_MSTestRunner_Standalone_Default_Extensions_Data))] public async Task RunTests_With_MSTestRunner_Standalone_Enable_Default_Extensions(string multiTfm, BuildConfiguration buildConfiguration, bool enableDefaultExtensions) { using TestAsset testAsset = await TestAsset.GenerateAssetAsync( @@ -248,12 +256,12 @@ public async Task RunTests_With_MSTestRunner_Standalone_Enable_Default_Extension .PatchCodeWithReplace("$TargetFramework$", multiTfm) .PatchCodeWithReplace("$ExtraProperties$", enableDefaultExtensions ? string.Empty : "None")); - DotnetMuxerResult compilationResult = await DotnetCli.RunAsync($"build -c {buildConfiguration} {testAsset.TargetAssetPath}", AcceptanceFixture.NuGetGlobalPackagesFolder.Path); - Assert.AreEqual(0, compilationResult.ExitCode); + DotnetMuxerResult compilationResult = await DotnetCli.RunAsync($"build -c {buildConfiguration} {testAsset.TargetAssetPath}", AcceptanceFixture.NuGetGlobalPackagesFolder.Path, cancellationToken: TestContext.CancellationToken); + compilationResult.AssertExitCodeIs(0); foreach (string tfm in multiTfm.Split(";")) { var testHost = TestHost.LocateFrom(testAsset.TargetAssetPath, AssetName, tfm, buildConfiguration: buildConfiguration); - TestHostResult testHostResult = await testHost.ExecuteAsync(command: "--coverage --report-trx"); + TestHostResult testHostResult = await testHost.ExecuteAsync(command: "--coverage --report-trx", cancellationToken: TestContext.CancellationToken); if (enableDefaultExtensions) { testHostResult.AssertOutputContainsSummary(0, 1, 0); @@ -266,7 +274,7 @@ public async Task RunTests_With_MSTestRunner_Standalone_Enable_Default_Extension } [TestMethod] - [DynamicData(nameof(GetBuildMatrixMultiTfmFoldedBuildConfiguration), typeof(AcceptanceTestBase), DynamicDataSourceType.Method)] + [DynamicData(nameof(GetBuildMatrixMultiTfmFoldedBuildConfiguration), typeof(AcceptanceTestBase))] public async Task Invalid_TestingProfile_Name_Should_Fail(string multiTfm, BuildConfiguration buildConfiguration) { using TestAsset testAsset = await TestAsset.GenerateAssetAsync( @@ -276,8 +284,8 @@ public async Task Invalid_TestingProfile_Name_Should_Fail(string multiTfm, Build .PatchCodeWithReplace("$TargetFramework$", multiTfm) .PatchCodeWithReplace("$ExtraProperties$", "WrongName")); - DotnetMuxerResult compilationResult = await DotnetCli.RunAsync($"build -c {buildConfiguration} {testAsset.TargetAssetPath}", AcceptanceFixture.NuGetGlobalPackagesFolder.Path, failIfReturnValueIsNotZero: false); - Assert.AreEqual(1, compilationResult.ExitCode); + DotnetMuxerResult compilationResult = await DotnetCli.RunAsync($"build -c {buildConfiguration} {testAsset.TargetAssetPath}", AcceptanceFixture.NuGetGlobalPackagesFolder.Path, failIfReturnValueIsNotZero: false, cancellationToken: TestContext.CancellationToken); + compilationResult.AssertExitCodeIs(1); compilationResult.AssertOutputContains("Invalid value for property TestingExtensionsProfile. Valid values are 'Default', 'AllMicrosoft' and 'None'."); } @@ -311,13 +319,12 @@ public async Task NativeAot_Smoke_Test_Windows() $"publish -r {RID} -f net9.0 {testAsset.TargetAssetPath}", AcceptanceFixture.NuGetGlobalPackagesFolder.Path, // We prefer to use the outer retry mechanism as we need some extra checks - retryCount: 0, - timeoutInSeconds: 180); + retryCount: 0, cancellationToken: TestContext.CancellationToken); compilationResult.AssertOutputContains("Generating native code"); compilationResult.AssertOutputDoesNotContain("warning"); var testHost = TestHost.LocateFrom(testAsset.TargetAssetPath, AssetName, "net9.0", verb: Verb.publish); - TestHostResult testHostResult = await testHost.ExecuteAsync(); + TestHostResult testHostResult = await testHost.ExecuteAsync(cancellationToken: TestContext.CancellationToken); testHostResult.AssertExitCodeIs(ExitCodes.Success); testHostResult.AssertOutputContainsSummary(0, 1, 0); @@ -328,7 +335,7 @@ public async Task NativeAot_Smoke_Test_Windows() public async Task EnablePlaywrightProperty_WhenUsingRunner_AllowsToRunPlaywrightTests(string tfm) { var testHost = TestHost.LocateFrom(AssetFixture.PlaywrightProjectPath, TestAssetFixture.PlaywrightProjectName, tfm); - TestHostResult testHostResult = await testHost.ExecuteAsync(); + TestHostResult testHostResult = await testHost.ExecuteAsync(cancellationToken: TestContext.CancellationToken); // Depending on the machine, the test might fail due to the browser not being installed. // To avoid slowing down the tests, we will not run the installation so depending on machines we have different results. @@ -362,7 +369,8 @@ public async Task EnablePlaywrightProperty_WhenUsingVSTest_AllowsToRunPlaywright workingDirectory: AssetFixture.PlaywrightProjectPath, failIfReturnValueIsNotZero: false, warnAsError: false, - suppressPreviewDotNetMessage: false); + suppressPreviewDotNetMessage: false, + cancellationToken: TestContext.CancellationToken); // Ensure output contains the right platform banner dotnetTestResult.AssertOutputContains("VSTest version"); @@ -389,7 +397,7 @@ public async Task EnablePlaywrightProperty_WhenUsingVSTest_AllowsToRunPlaywright public async Task EnableAspireProperty_WhenUsingRunner_AllowsToRunAspireTests() { var testHost = TestHost.LocateFrom(AssetFixture.AspireProjectPath, TestAssetFixture.AspireProjectName, TargetFrameworks.NetCurrent); - TestHostResult testHostResult = await testHost.ExecuteAsync(); + TestHostResult testHostResult = await testHost.ExecuteAsync(cancellationToken: TestContext.CancellationToken); testHostResult.AssertOutputContainsSummary(0, 1, 0); } @@ -405,8 +413,9 @@ public async Task EnableAspireProperty_WhenUsingVSTest_AllowsToRunAspireTests() AcceptanceFixture.NuGetGlobalPackagesFolder.Path, workingDirectory: AssetFixture.AspireProjectPath, warnAsError: false, - suppressPreviewDotNetMessage: false); - Assert.AreEqual(0, dotnetTestResult.ExitCode); + suppressPreviewDotNetMessage: false, + cancellationToken: TestContext.CancellationToken); + dotnetTestResult.AssertExitCodeIs(0); // Ensure output contains the right platform banner dotnetTestResult.AssertOutputContains("VSTest version"); dotnetTestResult.AssertOutputContains("Passed! - Failed: 0, Passed: 1, Skipped: 0, Total: 1"); @@ -422,13 +431,13 @@ public async Task SettingIsTestApplicationToFalseReducesAddedExtensionsAndMakesP .PatchCodeWithReplace("$TargetFramework$", TargetFrameworks.NetCurrent) .PatchCodeWithReplace("$ExtraProperties$", "false")); - DotnetMuxerResult compilationResult = await DotnetCli.RunAsync($"test {testAsset.TargetAssetPath}", AcceptanceFixture.NuGetGlobalPackagesFolder.Path, workingDirectory: testAsset.TargetAssetPath); + DotnetMuxerResult compilationResult = await DotnetCli.RunAsync($"test {testAsset.TargetAssetPath}", AcceptanceFixture.NuGetGlobalPackagesFolder.Path, workingDirectory: testAsset.TargetAssetPath, cancellationToken: TestContext.CancellationToken); - Assert.AreEqual(0, compilationResult.ExitCode); + compilationResult.AssertExitCodeIs(0); SL.Build binLog = SL.Serialization.Read(compilationResult.BinlogPath!); SL.Task cscTask = binLog.FindChildrenRecursive(task => task.Name == "Csc").Single(); - SL.Item[] references = cscTask.FindChildrenRecursive(p => p.Name == "References").Single().Children.OfType().ToArray(); + SL.Item[] references = [.. cscTask.FindChildrenRecursive(p => p.Name == "References").Single().Children.OfType()]; // Ensure that MSTest.Framework is referenced Assert.IsTrue(references.Any(r => r.Text.EndsWith("Microsoft.VisualStudio.TestPlatform.TestFramework.dll", StringComparison.OrdinalIgnoreCase))); @@ -467,9 +476,12 @@ public sealed class TestAssetFixture() : TestAssetFixtureBase(AcceptanceFixture. -#file dotnet.config -[dotnet.test.runner] -name= "VSTest" +#file global.json +{ + "test": { + "runner": "VSTest" + } +} #file UnitTest1.cs namespace AspireProject; @@ -534,9 +546,12 @@ public async Task HomepageHasPlaywrightInTitleAndGetStartedLinkLinkingToTheIntro } } -#file dotnet.config -[dotnet.test.runner] -name= "VSTest" +#file global.json +{ + "test": { + "runner": "VSTest" + } +} """; public string AspireProjectPath => GetAssetPath(AspireProjectName); @@ -556,4 +571,6 @@ public async Task HomepageHasPlaywrightInTitleAndGetStartedLinkLinkingToTheIntro .PatchCodeWithReplace("$MSTestVersion$", MSTestVersion)); } } + + public TestContext TestContext { get; set; } } diff --git a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/ServerModeTests.cs b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/ServerModeTests.cs index b79c30d152..d6ce030865 100644 --- a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/ServerModeTests.cs +++ b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/ServerModeTests.cs @@ -11,14 +11,16 @@ namespace MSTest.Acceptance.IntegrationTests; [TestClass] public sealed class ServerModeTests : ServerModeTestsBase { + public TestContext TestContext { get; set; } + [TestMethod] [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] public async Task DiscoverAndRun(string tfm) { using TestingPlatformClient jsonClient = await StartAsServerAndConnectToTheClientAsync(TestHost.LocateFrom(AssetFixture.ProjectPath, "MSTestProject", tfm, buildConfiguration: BuildConfiguration.Release)); - LogsCollector logs = new(); + LogsCollector logs = []; jsonClient.RegisterLogListener(logs); - TelemetryCollector telemetry = new(); + TelemetryCollector telemetry = []; jsonClient.RegisterTelemetryListener(telemetry); InitializeResponse initializeResponseArgs = await jsonClient.Initialize(); @@ -35,11 +37,11 @@ public async Task DiscoverAndRun(string tfm) await Task.WhenAll(discoveryListener.WaitCompletion(), runListener.WaitCompletion()); Assert.AreEqual(1, discoveryCollector.TestNodeUpdates.Count(x => x.Node.NodeType == "action"), "Wrong number of discovery"); - Assert.AreEqual(2, runCollector.TestNodeUpdates.Count, "Wrong number of updates"); + Assert.HasCount(2, runCollector.TestNodeUpdates); Assert.AreNotEqual(0, logs.Count, "Logs are empty"); Assert.IsFalse(telemetry.IsEmpty, "telemetry is empty"); await jsonClient.Exit(); - Assert.AreEqual(0, await jsonClient.WaitServerProcessExit()); + Assert.AreEqual(0, await jsonClient.WaitServerProcessExit(TestContext.CancellationToken)); Assert.AreEqual(0, jsonClient.ExitCode); } @@ -48,9 +50,9 @@ public async Task DiscoverAndRun(string tfm) public async Task WhenClientDies_Server_ShouldClose_Gracefully(string tfm) { using TestingPlatformClient jsonClient = await StartAsServerAndConnectToTheClientAsync(TestHost.LocateFrom(AssetFixture.ProjectPath, "MSTestProject", tfm, buildConfiguration: BuildConfiguration.Release)); - LogsCollector logs = new(); + LogsCollector logs = []; jsonClient.RegisterLogListener(logs); - TelemetryCollector telemetry = new(); + TelemetryCollector telemetry = []; jsonClient.RegisterTelemetryListener(telemetry); InitializeResponse initializeResponseArgs = await jsonClient.Initialize(); @@ -63,7 +65,7 @@ public async Task WhenClientDies_Server_ShouldClose_Gracefully(string tfm) _ = jsonClient.DiscoverTests(Guid.NewGuid(), discoveryCollector.CollectNodeUpdates, @checked: false); await jsonClient.Exit(gracefully: false); - int exitCode = await jsonClient.WaitServerProcessExit(); + int exitCode = await jsonClient.WaitServerProcessExit(TestContext.CancellationToken); Assert.AreEqual(3, exitCode); } diff --git a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/TestContextTests.cs b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/TestContextTests.cs index 7b86cfbcb4..2da2326fd3 100644 --- a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/TestContextTests.cs +++ b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/TestContextTests.cs @@ -10,10 +10,11 @@ namespace MSTest.Acceptance.IntegrationTests; public sealed class TestContextTests : AcceptanceTestBase { [TestMethod] - public async Task TestContextsAreCorrectlySet() + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] + public async Task TestContextsAreCorrectlySet(string tfm) { - var testHost = TestHost.LocateFrom(AssetFixture.ProjectPath, TestAssetFixture.ProjectName, TargetFrameworks.NetCurrent); - TestHostResult testHostResult = await testHost.ExecuteAsync("--filter ClassName~TestContextCtor"); + var testHost = TestHost.LocateFrom(AssetFixture.ProjectPath, TestAssetFixture.ProjectName, tfm); + TestHostResult testHostResult = await testHost.ExecuteAsync("--filter ClassName~TestContextCtor", cancellationToken: TestContext.CancellationToken); // Assert testHostResult.AssertExitCodeIs(0); @@ -24,7 +25,7 @@ public async Task TestContextsAreCorrectlySet() public async Task TestContext_TestData_PropertyContainsExpectedValue() { var testHost = TestHost.LocateFrom(AssetFixture.ProjectPath, TestAssetFixture.ProjectName, TargetFrameworks.NetCurrent); - TestHostResult testHostResult = await testHost.ExecuteAsync("--filter ClassName~TestContextData"); + TestHostResult testHostResult = await testHost.ExecuteAsync("--filter ClassName~TestContextData", cancellationToken: TestContext.CancellationToken); // Assert testHostResult.AssertExitCodeIs(0); @@ -35,7 +36,7 @@ public async Task TestContext_TestData_PropertyContainsExpectedValue() public async Task TestContext_TestException_PropertyContainsExpectedValue() { var testHost = TestHost.LocateFrom(AssetFixture.ProjectPath, TestAssetFixture.ProjectName, TargetFrameworks.NetCurrent); - TestHostResult testHostResult = await testHost.ExecuteAsync("--filter ClassName~TestContextException"); + TestHostResult testHostResult = await testHost.ExecuteAsync("--filter ClassName~TestContextException", cancellationToken: TestContext.CancellationToken); // Assert testHostResult.AssertExitCodeIs(2); @@ -48,7 +49,7 @@ public async Task TestContext_TestException_PropertyContainsExpectedValue() public async Task TestContext_TestDisplayName_PropertyContainsExpectedValue() { var testHost = TestHost.LocateFrom(AssetFixture.ProjectPath, TestAssetFixture.ProjectName, TargetFrameworks.NetCurrent); - TestHostResult testHostResult = await testHost.ExecuteAsync("--filter ClassName~TestContextDisplayName"); + TestHostResult testHostResult = await testHost.ExecuteAsync("--filter ClassName~TestContextDisplayName", cancellationToken: TestContext.CancellationToken); // Assert testHostResult.AssertExitCodeIs(0); @@ -59,7 +60,7 @@ public async Task TestContext_TestDisplayName_PropertyContainsExpectedValue() public async Task TestContext_Properties_ConsidersClassTypeCorrectly() { var testHost = TestHost.LocateFrom(AssetFixture.ProjectPath, TestAssetFixture.ProjectName, TargetFrameworks.NetCurrent); - TestHostResult testHostResult = await testHost.ExecuteAsync("--filter ClassName~TestContextTestPropertyImpl"); + TestHostResult testHostResult = await testHost.ExecuteAsync("--filter ClassName~TestContextTestPropertyImpl", cancellationToken: TestContext.CancellationToken); // Assert testHostResult.AssertExitCodeIs(0); @@ -76,7 +77,7 @@ public sealed class TestAssetFixture() : TestAssetFixtureBase(AcceptanceFixture. { yield return (ProjectName, ProjectName, SourceCode - .PatchTargetFrameworks(TargetFrameworks.NetCurrent) + .PatchTargetFrameworks(TargetFrameworks.All) .PatchCodeWithReplace("$MSTestVersion$", MSTestVersion)); } @@ -88,6 +89,7 @@ public sealed class TestAssetFixture() : TestAssetFixtureBase(AcceptanceFixture. Exe true $TargetFrameworks$ + preview - false + true + true Exe + + + Analyzer + false + + + Analyzer + false + + + Analyzer + false + diff --git a/test/IntegrationTests/MSTest.VstestConsoleWrapper.IntegrationTests/ParallelExecutionTests.cs b/test/IntegrationTests/MSTest.VstestConsoleWrapper.IntegrationTests/ParallelExecutionTests.cs index f2af2f9661..6acadc5164 100644 --- a/test/IntegrationTests/MSTest.VstestConsoleWrapper.IntegrationTests/ParallelExecutionTests.cs +++ b/test/IntegrationTests/MSTest.VstestConsoleWrapper.IntegrationTests/ParallelExecutionTests.cs @@ -1,12 +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 FluentAssertions.Execution; - using Microsoft.MSTestV2.CLIAutomation; namespace MSTest.VstestConsoleWrapper.IntegrationTests; +[TestClass] public class ParallelExecutionTests : CLITestBase { private const string ClassParallelTestAssetName = "ParallelClassesTestProject"; @@ -15,6 +14,7 @@ public class ParallelExecutionTests : CLITestBase private const int TestMethodWaitTimeInMS = 1000; private const int OverheadTimeInMS = 4000; + [TestMethod] public async Task AllMethodsShouldRunInParallel() { const int maxAttempts = 10; @@ -31,9 +31,9 @@ public async Task AllMethodsShouldRunInParallel() } // Timer validation sometimes get flacky. So retrying the test if it fails. - catch (AssertionFailedException ex) when (i != maxAttempts && ex.Message.Contains("Test Run was expected to not exceed")) + catch (AssertFailedException ex) when (i != maxAttempts && ex.Message.Contains("Test Run was expected to not exceed")) { - await Task.Delay(2000); + await Task.Delay(2000, TestContext.CancellationToken); } } @@ -48,6 +48,7 @@ public async Task AllMethodsShouldRunInParallel() "ParallelMethodsTestProject.UnitTest2.SimpleTest22"); } + [TestMethod] public void AllClassesShouldRunInParallel() { InvokeVsTestForExecution([ClassParallelTestAssetName]); @@ -70,6 +71,7 @@ public void AllClassesShouldRunInParallel() "ParallelClassesTestProject.UnitTest3.SimpleTest32"); } + [TestMethod] public void NothingShouldRunInParallel() { const string RunSetting = @@ -102,4 +104,6 @@ public void NothingShouldRunInParallel() "DoNotParallelizeTestProject.UnitTest1.SimpleTest12", "DoNotParallelizeTestProject.UnitTest2.SimpleTest22"); } + + public TestContext TestContext { get; set; } } diff --git a/test/IntegrationTests/MSTest.VstestConsoleWrapper.IntegrationTests/Parameterized tests/DataExtensibilityTests.cs b/test/IntegrationTests/MSTest.VstestConsoleWrapper.IntegrationTests/Parameterized tests/DataExtensibilityTests.cs index 44f953da41..572568187c 100644 --- a/test/IntegrationTests/MSTest.VstestConsoleWrapper.IntegrationTests/Parameterized tests/DataExtensibilityTests.cs +++ b/test/IntegrationTests/MSTest.VstestConsoleWrapper.IntegrationTests/Parameterized tests/DataExtensibilityTests.cs @@ -5,10 +5,12 @@ namespace MSTest.VstestConsoleWrapper.IntegrationTests; +[TestClass] public class DataExtensibilityTests : CLITestBase { private const string TestAssetName = "FxExtensibilityTestProject"; + [TestMethod] public void ExecuteTestDataSourceExtensibilityTests() { InvokeVsTestForExecution([TestAssetName]); @@ -16,6 +18,7 @@ public void ExecuteTestDataSourceExtensibilityTests() ValidateFailedTestsContain(false, "FxExtensibilityTestProject.TestDataSourceExTests.CustomEmptyTestDataSourceTestMethod"); } + [TestMethod] public void ExecuteDynamicDataExtensibilityTests() { InvokeVsTestForExecution([TestAssetName]); @@ -41,6 +44,7 @@ public void ExecuteDynamicDataExtensibilityTests() "FxExtensibilityTestProject.DynamicDataExMoreTests.DynamicEmptyDataTestMethod6"); } + [TestMethod] public void ExecuteCustomTestExtensibilityTests() { InvokeVsTestForExecution([TestAssetName]); @@ -60,6 +64,7 @@ public void ExecuteCustomTestExtensibilityTests() "CustomTestClass1 - Execution number 3"); } + [TestMethod] public void ExecuteCustomTestExtensibilityWithTestDataTests() { InvokeVsTestForExecution([TestAssetName], testCaseFilter: "FullyQualifiedName~CustomTestExTests.CustomTestMethod2"); diff --git a/test/IntegrationTests/MSTest.VstestConsoleWrapper.IntegrationTests/Parameterized tests/DataRowTests.cs b/test/IntegrationTests/MSTest.VstestConsoleWrapper.IntegrationTests/Parameterized tests/DataRowTests.cs index 1af7da9450..0493b83be6 100644 --- a/test/IntegrationTests/MSTest.VstestConsoleWrapper.IntegrationTests/Parameterized tests/DataRowTests.cs +++ b/test/IntegrationTests/MSTest.VstestConsoleWrapper.IntegrationTests/Parameterized tests/DataRowTests.cs @@ -5,10 +5,12 @@ namespace MSTest.VstestConsoleWrapper.IntegrationTests; +[TestClass] public class DataRowTests : CLITestBase { private const string TestAssetName = "DataRowTestProject"; + [TestMethod] public void ExecuteOnlyDerivedClassDataRowsWhenBothBaseAndDerivedClassHasDataRows_SimpleDataRows() { InvokeVsTestForExecution([TestAssetName], testCaseFilter: "TestCategory~DataRowSimple"); @@ -26,6 +28,7 @@ public void ExecuteOnlyDerivedClassDataRowsWhenBothBaseAndDerivedClassHasDataRow ValidatePassedTestsCount(5); } + [TestMethod] public void GetDisplayName_AfterOverriding_GetsTheNewDisplayName() { InvokeVsTestForExecution([TestAssetName], testCaseFilter: "TestCategory~OverriddenGetDisplayName"); @@ -36,6 +39,7 @@ public void GetDisplayName_AfterOverriding_GetsTheNewDisplayName() ValidatePassedTestsCount(1); } + [TestMethod] public void ParameterizedTestsWithTestMethodSettingDisplayName_DataIsPrefixWithDisplayName() { InvokeVsTestForExecution([TestAssetName], testCaseFilter: "TestCategory~OverriddenTestMethodDisplayNameForParameterizedTest"); @@ -47,6 +51,7 @@ public void ParameterizedTestsWithTestMethodSettingDisplayName_DataIsPrefixWithD ValidatePassedTestsCount(2); } + [TestMethod] public void ExecuteOnlyDerivedClassDataRowsWhenItOverridesBaseClassDataRows_SimpleDataRows() { InvokeVsTestForExecution([TestAssetName], testCaseFilter: "FullyQualifiedName~DerivedClass&TestCategory~DataRowSimple"); @@ -59,6 +64,7 @@ public void ExecuteOnlyDerivedClassDataRowsWhenItOverridesBaseClassDataRows_Simp ValidatePassedTestsCount(2); } + [TestMethod] public void DataRowsExecuteWithRequiredAndOptionalParameters() { InvokeVsTestForExecution([TestAssetName], testCaseFilter: "TestCategory~DataRowSomeOptional"); @@ -72,6 +78,7 @@ public void DataRowsExecuteWithRequiredAndOptionalParameters() ValidatePassedTestsCount(3); } + [TestMethod] public void DataRowsExecuteWithAllOptionalParameters() { InvokeVsTestForExecution([TestAssetName], testCaseFilter: "TestCategory~DataRowAllOptional"); @@ -86,6 +93,7 @@ public void DataRowsExecuteWithAllOptionalParameters() ValidatePassedTestsCount(4); } + [TestMethod] public void DataRowsExecuteWithParamsArrayParameter() { InvokeVsTestForExecution([TestAssetName], testCaseFilter: "TestCategory~DataRowParamsArgument"); @@ -100,6 +108,7 @@ public void DataRowsExecuteWithParamsArrayParameter() ValidatePassedTestsCount(4); } + [TestMethod] public void DataRowsFailWhenInvalidArgumentsProvided() { InvokeVsTestForExecution([TestAssetName], testCaseFilter: "FullyQualifiedName~DataRowTests_Regular&TestCategory~DataRowOptionalInvalidArguments"); @@ -114,6 +123,7 @@ public void DataRowsFailWhenInvalidArgumentsProvided() ValidateFailedTestsCount(3); } + [TestMethod] public void ExecuteDataRowTests_Enums() { // Arrange & Act @@ -186,6 +196,7 @@ public void ExecuteDataRowTests_Enums() ValidateFailedTestsCount(0); } + [TestMethod] public void ExecuteDataRowTests_NonSerializablePaths() { // Arrange & Act @@ -202,6 +213,7 @@ public void ExecuteDataRowTests_NonSerializablePaths() ValidateFailedTestsCount(0); } + [TestMethod] public void ExecuteRegular_DataRowTests() { // Arrange & Act diff --git a/test/IntegrationTests/MSTest.VstestConsoleWrapper.IntegrationTests/Parameterized tests/DataSourceTests.cs b/test/IntegrationTests/MSTest.VstestConsoleWrapper.IntegrationTests/Parameterized tests/DataSourceTests.cs index 31a41e6d4a..80ff920ce3 100644 --- a/test/IntegrationTests/MSTest.VstestConsoleWrapper.IntegrationTests/Parameterized tests/DataSourceTests.cs +++ b/test/IntegrationTests/MSTest.VstestConsoleWrapper.IntegrationTests/Parameterized tests/DataSourceTests.cs @@ -5,13 +5,14 @@ namespace MSTest.VstestConsoleWrapper.IntegrationTests; +[TestClass] public class DataSourceTests : CLITestBase { private const string TestAssetName = "DataSourceTestProject"; - // TODO @haplois | @evangelink - [SuppressMessage("CodeQuality", "IDE0051:Remove unused private members", Justification = "This test fails under CI - will be fixed in a future PR (marked as private to ignore the test)")] - private void ExecuteCsvTestDataSourceTests() + [TestMethod] + [Ignore("This test fails under CI - will be fixed in a future PR (marked as private to ignore the test)")] + public void ExecuteCsvTestDataSourceTests() { // Arrange & Act InvokeVsTestForExecution( diff --git a/test/IntegrationTests/MSTest.VstestConsoleWrapper.IntegrationTests/Parameterized tests/DynamicDataTests.cs b/test/IntegrationTests/MSTest.VstestConsoleWrapper.IntegrationTests/Parameterized tests/DynamicDataTests.cs index ab9083a04b..bcd2386251 100644 --- a/test/IntegrationTests/MSTest.VstestConsoleWrapper.IntegrationTests/Parameterized tests/DynamicDataTests.cs +++ b/test/IntegrationTests/MSTest.VstestConsoleWrapper.IntegrationTests/Parameterized tests/DynamicDataTests.cs @@ -5,10 +5,12 @@ namespace MSTest.VstestConsoleWrapper.IntegrationTests; +[TestClass] public class DynamicDataTests : CLITestBase { private const string TestAssetName = "DynamicDataTestProject"; + [TestMethod] public void ExecuteDynamicDataTests() { // Arrange & Act @@ -71,7 +73,11 @@ public void ExecuteDynamicDataTests() "MethodWithOverload (2,\"2\")", "DynamicDataTest_SimpleCollection (0)", "DynamicDataTest_SimpleCollection (2)", - "DynamicDataTest_SimpleCollection (4)"); + "DynamicDataTest_SimpleCollection (4)", + "DynamicDataTest_SourceFieldExplicit (\"field\",5)", + "DynamicDataTest_SourceFieldExplicit (\"test\",4)", + "DynamicDataTest_SourceFieldAutoDetect (\"field\",5)", + "DynamicDataTest_SourceFieldAutoDetect (\"test\",4)"); ValidateFailedTestsCount(0); } diff --git a/test/IntegrationTests/MSTest.VstestConsoleWrapper.IntegrationTests/Program.cs b/test/IntegrationTests/MSTest.VstestConsoleWrapper.IntegrationTests/Program.cs index ce5734f730..e1b9e97e60 100644 --- a/test/IntegrationTests/MSTest.VstestConsoleWrapper.IntegrationTests/Program.cs +++ b/test/IntegrationTests/MSTest.VstestConsoleWrapper.IntegrationTests/Program.cs @@ -3,7 +3,7 @@ using Microsoft.Testing.Extensions; -using TestFramework.ForTestingMSTest; +[assembly: DoNotParallelize] ITestApplicationBuilder builder = await TestApplication.CreateBuilderAsync(args); @@ -16,7 +16,7 @@ builder.AddRetryProvider(); builder.AddAzureDevOpsProvider(); -builder.AddInternalTestFramework(); +builder.AddMSTest(() => [Assembly.GetEntryAssembly()!]); ITestApplication app = await builder.BuildAsync(); return await app.RunAsync(); diff --git a/test/IntegrationTests/MSTest.VstestConsoleWrapper.IntegrationTests/SuiteLifeCycleTests.cs b/test/IntegrationTests/MSTest.VstestConsoleWrapper.IntegrationTests/SuiteLifeCycleTests.cs index c46587f782..605adeb1d0 100644 --- a/test/IntegrationTests/MSTest.VstestConsoleWrapper.IntegrationTests/SuiteLifeCycleTests.cs +++ b/test/IntegrationTests/MSTest.VstestConsoleWrapper.IntegrationTests/SuiteLifeCycleTests.cs @@ -1,155 +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 FluentAssertions; - using Microsoft.MSTestV2.CLIAutomation; +using Microsoft.VisualStudio.TestPlatform.ObjectModel; + +using TestResult = Microsoft.VisualStudio.TestPlatform.ObjectModel.TestResult; namespace MSTest.VstestConsoleWrapper.IntegrationTests; +[TestClass] public class SuiteLifeCycleTests : CLITestBase { private const string TestAssetName = "SuiteLifeCycleTestProject"; private static readonly string[] WindowsLineReturn = ["\r\n"]; - public void ValidateTestRunLifecycle_net6() => ValidateTestRunLifecycle("net6.0"); - - public void ValidateTestRunLifecycle_net462() => ValidateTestRunLifecycle("net462"); - - public void ValidateInheritanceBehavior() - { - InvokeVsTestForExecution( - [TestAssetName], - testCaseFilter: "FullyQualifiedName~LifecycleInheritance", - targetFramework: "net462"); - - RunEventsHandler.PassedTests.Should().HaveCount(10); - - Microsoft.VisualStudio.TestPlatform.ObjectModel.TestResult testMethod1 = RunEventsHandler.PassedTests.Single(x => x.TestCase.FullyQualifiedName.EndsWith("TestClassDerived_EndOfClass.TestMethod", StringComparison.Ordinal)); - testMethod1.Messages[0].Text.Should().Be( - """ - Console: AssemblyInit was called - TestClassBaseEndOfClass: ClassInitialize - TestClassDerived_EndOfClass: TestMethod - TestClassBaseEndOfClass: ClassCleanup - - """); - - Microsoft.VisualStudio.TestPlatform.ObjectModel.TestResult testMethod2 = RunEventsHandler.PassedTests.Single(x => x.TestCase.FullyQualifiedName.EndsWith("TestClassDerived_EndOfAssembly.TestMethod", StringComparison.Ordinal)); - testMethod2.Messages[0].Text.Should().Be( - """ - TestClassBaseEndOfAssembly: ClassInitialize - TestClassDerived_EndOfAssembly: TestMethod - - """); - - Microsoft.VisualStudio.TestPlatform.ObjectModel.TestResult testMethod3 = RunEventsHandler.PassedTests.Single(x => x.TestCase.FullyQualifiedName.EndsWith("TestClassDerivedEndOfClass_EndOfClassEndOfClass.TestMethod", StringComparison.Ordinal)); - testMethod3.Messages[0].Text.Should().Be( - """ - TestClassBaseEndOfClass: ClassInitialize - TestClassIntermediateEndOfClassBaseEndOfClass: ClassInitialize - TestClassDerivedEndOfClass_EndOfClassEndOfClass: TestMethod - TestClassDerivedEndOfClass_EndOfClassEndOfClass: ClassCleanup - TestClassIntermediateEndOfClassBaseEndOfClass: ClassCleanup - TestClassBaseEndOfClass: ClassCleanup - - """); - - Microsoft.VisualStudio.TestPlatform.ObjectModel.TestResult testMethod4 = RunEventsHandler.PassedTests.Single(x => x.TestCase.FullyQualifiedName.EndsWith("TestClassDerived_EndOfClassEndOfClass.TestMethod", StringComparison.Ordinal)); - testMethod4.Messages[0].Text.Should().Be( - """ - TestClassBaseEndOfClass: ClassInitialize - TestClassIntermediateEndOfClassBaseEndOfClass: ClassInitialize - TestClassDerived_EndOfClassEndOfClass: TestMethod - TestClassIntermediateEndOfClassBaseEndOfClass: ClassCleanup - TestClassBaseEndOfClass: ClassCleanup - - """); - - Microsoft.VisualStudio.TestPlatform.ObjectModel.TestResult testMethod5 = RunEventsHandler.PassedTests.Single(x => x.TestCase.FullyQualifiedName.EndsWith("TestClassDerivedEndOfClass_EndOfClassEndOfAssembly.TestMethod", StringComparison.Ordinal)); - testMethod5.Messages[0].Text.Should().Be( - """ - TestClassBaseEndOfAssembly: ClassInitialize - TestClassIntermediateEndOfClassBaseEndOfAssembly: ClassInitialize - TestClassDerivedEndOfClass_EndOfClassEndOfAssembly: TestMethod - TestClassDerivedEndOfClass_EndOfClassEndOfAssembly: ClassCleanup - TestClassIntermediateEndOfClassBaseEndOfAssembly: ClassCleanup - TestClassBaseEndOfAssembly: ClassCleanup - - """); - - Microsoft.VisualStudio.TestPlatform.ObjectModel.TestResult testMethod6 = RunEventsHandler.PassedTests.Single(x => x.TestCase.FullyQualifiedName.EndsWith("TestClassDerived_EndOfClassEndOfAssembly.TestMethod", StringComparison.Ordinal)); - testMethod6.Messages[0].Text.Should().Be( - """ - TestClassBaseEndOfAssembly: ClassInitialize - TestClassIntermediateEndOfClassBaseEndOfAssembly: ClassInitialize - TestClassDerived_EndOfClassEndOfAssembly: TestMethod - TestClassIntermediateEndOfClassBaseEndOfAssembly: ClassCleanup - TestClassBaseEndOfAssembly: ClassCleanup - - """); - - Microsoft.VisualStudio.TestPlatform.ObjectModel.TestResult testMethod7 = RunEventsHandler.PassedTests.Single(x => x.TestCase.FullyQualifiedName.EndsWith("TestClassDerivedEndOfClass_EndOfAssemblyEndOfClass.TestMethod", StringComparison.Ordinal)); - testMethod7.Messages[0].Text.Should().Be( - """ - TestClassBaseEndOfClass: ClassInitialize - TestClassIntermediateEndOfAssemblyBaseEndOfClass: ClassInitialize - TestClassDerivedEndOfClass_EndOfAssemblyEndOfClass: TestMethod - TestClassDerivedEndOfClass_EndOfAssemblyEndOfClass: ClassCleanup - TestClassIntermediateEndOfAssemblyBaseEndOfClass: ClassCleanup - TestClassBaseEndOfClass: ClassCleanup - - """); - - Microsoft.VisualStudio.TestPlatform.ObjectModel.TestResult testMethod8 = RunEventsHandler.PassedTests.Single(x => x.TestCase.FullyQualifiedName.EndsWith("TestClassDerived_EndOfAssemblyEndOfClass.TestMethod", StringComparison.Ordinal)); - testMethod8.Messages[0].Text.Should().Be( - """ - TestClassBaseEndOfClass: ClassInitialize - TestClassIntermediateEndOfAssemblyBaseEndOfClass: ClassInitialize - TestClassDerived_EndOfAssemblyEndOfClass: TestMethod - TestClassIntermediateEndOfAssemblyBaseEndOfClass: ClassCleanup - TestClassBaseEndOfClass: ClassCleanup - - """); - - Microsoft.VisualStudio.TestPlatform.ObjectModel.TestResult testMethod9 = RunEventsHandler.PassedTests.Single(x => x.TestCase.FullyQualifiedName.EndsWith("TestClassDerivedEndOfClass_EndOfAssemblyEndOfAssembly.TestMethod", StringComparison.Ordinal)); - testMethod9.Messages[0].Text.Should().Be( - """ - TestClassBaseEndOfAssembly: ClassInitialize - TestClassIntermediateEndOfAssemblyBaseEndOfAssembly: ClassInitialize - TestClassDerivedEndOfClass_EndOfAssemblyEndOfAssembly: TestMethod - TestClassDerivedEndOfClass_EndOfAssemblyEndOfAssembly: ClassCleanup - TestClassIntermediateEndOfAssemblyBaseEndOfAssembly: ClassCleanup - TestClassBaseEndOfAssembly: ClassCleanup - - """); - - Microsoft.VisualStudio.TestPlatform.ObjectModel.TestResult testMethod10 = RunEventsHandler.PassedTests.Single(x => x.TestCase.FullyQualifiedName.EndsWith("TestClassDerived_EndOfAssemblyEndOfAssembly.TestMethod", StringComparison.Ordinal)); - testMethod10.Messages[0].Text.Should().Be( - """ - TestClassBaseEndOfAssembly: ClassInitialize - TestClassIntermediateEndOfAssemblyBaseEndOfAssembly: ClassInitialize - TestClassDerived_EndOfAssemblyEndOfAssembly: TestMethod - TestClassIntermediateEndOfAssemblyBaseEndOfAssembly: ClassCleanup - TestClassBaseEndOfAssembly: ClassCleanup - TestClassBaseEndOfAssembly: ClassCleanup - Console: AssemblyCleanup was called - - """); - } - - private void ValidateTestRunLifecycle(string targetFramework) + [TestMethod] + [DataRow("net462")] + [DataRow("net6.0")] + public void ValidateTestRunLifecycle(string targetFramework) { InvokeVsTestForExecution( [TestAssetName], testCaseFilter: "FullyQualifiedName~SuiteLifeCycleTestProject", targetFramework: targetFramework); - RunEventsHandler.PassedTests.Should().HaveCount(27); // The inherit class tests are called twice. + Assert.HasCount(27, RunEventsHandler.PassedTests); // The inherit class tests are called twice. - Microsoft.VisualStudio.TestPlatform.ObjectModel.TestResult caseClassCleanup = RunEventsHandler.PassedTests.Single(x => x.TestCase.FullyQualifiedName.Contains("LifeCycleClassCleanup.TestMethod")); - caseClassCleanup.Outcome.Should().Be(Microsoft.VisualStudio.TestPlatform.ObjectModel.TestOutcome.Passed); - caseClassCleanup.Messages.Should().HaveCount(3); - caseClassCleanup.Messages[0].Text.Should().Be( + TestResult caseClassCleanup = RunEventsHandler.PassedTests.Single(x => x.TestCase.FullyQualifiedName.Contains("LifeCycleClassCleanup.TestMethod")); + Assert.AreEqual(TestOutcome.Passed, caseClassCleanup.Outcome); + Assert.HasCount(3, caseClassCleanup.Messages); + Assert.AreEqual( $""" Console: AssemblyInit was called Console: LifeCycleClassCleanup.ClassInitialize was called @@ -161,8 +40,9 @@ private void ValidateTestRunLifecycle(string targetFramework) ? "Console: LifeCycleClassCleanup.DisposeAsync was called\r\nConsole: LifeCycleClassCleanup.Dispose was called" : "Console: LifeCycleClassCleanup.Dispose was called")} - """); - caseClassCleanup.Messages[1].Text.Should().Be( + """, + caseClassCleanup.Messages[0].Text); + Assert.AreEqual( $""" @@ -179,8 +59,9 @@ private void ValidateTestRunLifecycle(string targetFramework) + GenerateTraceDebugPrefixedMessage("LifeCycleClassCleanup.Dispose was called") : GenerateTraceDebugPrefixedMessage("LifeCycleClassCleanup.Dispose was called"))} - """); - caseClassCleanup.Messages[2].Text.Should().Be( + """, + caseClassCleanup.Messages[1].Text); + Assert.AreEqual( $""" @@ -195,15 +76,16 @@ LifeCycleClassCleanup.TestCleanup was called ? "LifeCycleClassCleanup.DisposeAsync was called\r\nLifeCycleClassCleanup.Dispose was called" : "LifeCycleClassCleanup.Dispose was called")} - """); + """, + caseClassCleanup.Messages[2].Text); - Microsoft.VisualStudio.TestPlatform.ObjectModel.TestResult caseClassCleanupEndOfAssembly = RunEventsHandler.PassedTests.Single(x => x.TestCase.FullyQualifiedName.Contains("LifeCycleClassCleanupEndOfAssembly.TestMethod")); - caseClassCleanupEndOfAssembly.Outcome.Should().Be(Microsoft.VisualStudio.TestPlatform.ObjectModel.TestOutcome.Passed); + TestResult caseClassCleanupEndOfAssembly = RunEventsHandler.PassedTests.Single(x => x.TestCase.FullyQualifiedName.Contains("LifeCycleClassCleanupEndOfAssembly.TestMethod")); + Assert.AreEqual(TestOutcome.Passed, caseClassCleanupEndOfAssembly.Outcome); // We don't see "LifeCycleClassCleanupEndOfAssembly.ClassCleanup was called" because it will be attached to the // latest test run. - caseClassCleanupEndOfAssembly.Messages.Should().HaveCount(3); - caseClassCleanupEndOfAssembly.Messages[0].Text.Should().Be( + Assert.HasCount(3, caseClassCleanupEndOfAssembly.Messages); + Assert.AreEqual( $""" Console: LifeCycleClassCleanupEndOfAssembly.ClassInitialize was called Console: LifeCycleClassCleanupEndOfAssembly.ctor was called @@ -214,8 +96,9 @@ LifeCycleClassCleanup.TestCleanup was called ? "Console: LifeCycleClassCleanupEndOfAssembly.DisposeAsync was called\r\nConsole: LifeCycleClassCleanupEndOfAssembly.Dispose was called" : "Console: LifeCycleClassCleanupEndOfAssembly.Dispose was called")} - """); - caseClassCleanupEndOfAssembly.Messages[1].Text.Should().Be( + """, + caseClassCleanupEndOfAssembly.Messages[0].Text); + Assert.AreEqual( $""" @@ -231,8 +114,9 @@ LifeCycleClassCleanup.TestCleanup was called + GenerateTraceDebugPrefixedMessage("LifeCycleClassCleanupEndOfAssembly.Dispose was called") : GenerateTraceDebugPrefixedMessage("LifeCycleClassCleanupEndOfAssembly.Dispose was called"))} - """); - caseClassCleanupEndOfAssembly.Messages[2].Text.Should().Be( + """, + caseClassCleanupEndOfAssembly.Messages[1].Text); + Assert.AreEqual( $""" @@ -246,12 +130,13 @@ LifeCycleClassCleanupEndOfAssembly.TestCleanup was called ? "LifeCycleClassCleanupEndOfAssembly.DisposeAsync was called\r\nLifeCycleClassCleanupEndOfAssembly.Dispose was called" : "LifeCycleClassCleanupEndOfAssembly.Dispose was called")} - """); + """, + caseClassCleanupEndOfAssembly.Messages[2].Text); - Microsoft.VisualStudio.TestPlatform.ObjectModel.TestResult caseClassCleanupEndOfClass = RunEventsHandler.PassedTests.Single(x => x.TestCase.FullyQualifiedName.Contains("LifeCycleClassCleanupEndOfClass.TestMethod")); - caseClassCleanupEndOfClass.Outcome.Should().Be(Microsoft.VisualStudio.TestPlatform.ObjectModel.TestOutcome.Passed); - caseClassCleanupEndOfClass.Messages.Should().HaveCount(3); - caseClassCleanupEndOfClass.Messages[0].Text.Should().Be( + TestResult caseClassCleanupEndOfClass = RunEventsHandler.PassedTests.Single(x => x.TestCase.FullyQualifiedName.Contains("LifeCycleClassCleanupEndOfClass.TestMethod")); + Assert.AreEqual(TestOutcome.Passed, caseClassCleanupEndOfClass.Outcome); + Assert.HasCount(3, caseClassCleanupEndOfClass.Messages); + Assert.AreEqual( $""" Console: LifeCycleClassCleanupEndOfClass.ClassInitialize was called Console: LifeCycleClassCleanupEndOfClass.ctor was called @@ -263,8 +148,9 @@ LifeCycleClassCleanupEndOfAssembly.TestCleanup was called : "Console: LifeCycleClassCleanupEndOfClass.Dispose was called")} Console: LifeCycleClassCleanupEndOfClass.ClassCleanup was called - """); - caseClassCleanupEndOfClass.Messages[1].Text.Should().Be( + """, + caseClassCleanupEndOfClass.Messages[0].Text); + Assert.AreEqual( $""" @@ -281,8 +167,9 @@ LifeCycleClassCleanupEndOfAssembly.TestCleanup was called : GenerateTraceDebugPrefixedMessage("LifeCycleClassCleanupEndOfClass.Dispose was called"))} {GenerateTraceDebugPrefixedMessage("LifeCycleClassCleanupEndOfClass.ClassCleanup was called")} - """); - caseClassCleanupEndOfClass.Messages[2].Text.Should().Be( + """, + caseClassCleanupEndOfClass.Messages[1].Text); + Assert.AreEqual( $""" @@ -297,12 +184,13 @@ LifeCycleClassCleanupEndOfClass.TestCleanup was called : "LifeCycleClassCleanupEndOfClass.Dispose was called")} LifeCycleClassCleanupEndOfClass.ClassCleanup was called - """); + """, + caseClassCleanupEndOfClass.Messages[2].Text); - Microsoft.VisualStudio.TestPlatform.ObjectModel.TestResult caseClassInitializeAndCleanupBeforeEachDerivedClass = RunEventsHandler.PassedTests.Single(x => x.TestCase.FullyQualifiedName.Contains("LifeCycleClassInitializeAndCleanupBeforeEachDerivedClass.TestMethod")); - caseClassInitializeAndCleanupBeforeEachDerivedClass.Outcome.Should().Be(Microsoft.VisualStudio.TestPlatform.ObjectModel.TestOutcome.Passed); - caseClassInitializeAndCleanupBeforeEachDerivedClass.Messages.Should().HaveCount(3); - caseClassInitializeAndCleanupBeforeEachDerivedClass.Messages[0].Text.Should().Be( + TestResult caseClassInitializeAndCleanupBeforeEachDerivedClass = RunEventsHandler.PassedTests.Single(x => x.TestCase.FullyQualifiedName.Contains("LifeCycleClassInitializeAndCleanupBeforeEachDerivedClass.TestMethod")); + Assert.AreEqual(TestOutcome.Passed, caseClassInitializeAndCleanupBeforeEachDerivedClass.Outcome); + Assert.HasCount(3, caseClassInitializeAndCleanupBeforeEachDerivedClass.Messages); + Assert.AreEqual( $""" Console: LifeCycleClassInitializeAndCleanupBeforeEachDerivedClass.ClassInitialize was called Console: LifeCycleClassInitializeAndCleanupBeforeEachDerivedClass.ctor was called @@ -313,8 +201,9 @@ LifeCycleClassCleanupEndOfClass.ClassCleanup was called ? "Console: LifeCycleClassInitializeAndCleanupBeforeEachDerivedClass.DisposeAsync was called\r\nConsole: LifeCycleClassInitializeAndCleanupBeforeEachDerivedClass.Dispose was called" : "Console: LifeCycleClassInitializeAndCleanupBeforeEachDerivedClass.Dispose was called")} - """); - caseClassInitializeAndCleanupBeforeEachDerivedClass.Messages[1].Text.Should().Be( + """, + caseClassInitializeAndCleanupBeforeEachDerivedClass.Messages[0].Text); + Assert.AreEqual( $""" @@ -330,8 +219,9 @@ LifeCycleClassCleanupEndOfClass.ClassCleanup was called + GenerateTraceDebugPrefixedMessage("LifeCycleClassInitializeAndCleanupBeforeEachDerivedClass.Dispose was called") : GenerateTraceDebugPrefixedMessage("LifeCycleClassInitializeAndCleanupBeforeEachDerivedClass.Dispose was called"))} - """); - caseClassInitializeAndCleanupBeforeEachDerivedClass.Messages[2].Text.Should().Be( + """, + caseClassInitializeAndCleanupBeforeEachDerivedClass.Messages[1].Text); + Assert.AreEqual( $""" @@ -345,12 +235,13 @@ LifeCycleClassInitializeAndCleanupBeforeEachDerivedClass.TestCleanup was called ? "LifeCycleClassInitializeAndCleanupBeforeEachDerivedClass.DisposeAsync was called\r\nLifeCycleClassInitializeAndCleanupBeforeEachDerivedClass.Dispose was called" : "LifeCycleClassInitializeAndCleanupBeforeEachDerivedClass.Dispose was called")} - """); + """, + caseClassInitializeAndCleanupBeforeEachDerivedClass.Messages[2].Text); - Microsoft.VisualStudio.TestPlatform.ObjectModel.TestResult caseClassInitializeAndCleanupNone = RunEventsHandler.PassedTests.Single(x => x.TestCase.FullyQualifiedName.Contains("LifeCycleClassInitializeAndCleanupNone.TestMethod")); - caseClassInitializeAndCleanupNone.Outcome.Should().Be(Microsoft.VisualStudio.TestPlatform.ObjectModel.TestOutcome.Passed); - caseClassInitializeAndCleanupNone.Messages.Should().HaveCount(3); - caseClassInitializeAndCleanupNone.Messages[0].Text.Should().Be( + TestResult caseClassInitializeAndCleanupNone = RunEventsHandler.PassedTests.Single(x => x.TestCase.FullyQualifiedName.Contains("LifeCycleClassInitializeAndCleanupNone.TestMethod")); + Assert.AreEqual(TestOutcome.Passed, caseClassInitializeAndCleanupNone.Outcome); + Assert.HasCount(3, caseClassInitializeAndCleanupNone.Messages); + Assert.AreEqual( $""" Console: LifeCycleClassInitializeAndCleanupNone.ClassInitialize was called Console: LifeCycleClassInitializeAndCleanupNone.ctor was called @@ -361,8 +252,9 @@ LifeCycleClassInitializeAndCleanupBeforeEachDerivedClass.TestCleanup was called ? "Console: LifeCycleClassInitializeAndCleanupNone.DisposeAsync was called\r\nConsole: LifeCycleClassInitializeAndCleanupNone.Dispose was called" : "Console: LifeCycleClassInitializeAndCleanupNone.Dispose was called")} - """); - caseClassInitializeAndCleanupNone.Messages[1].Text.Should().Be( + """, + caseClassInitializeAndCleanupNone.Messages[0].Text); + Assert.AreEqual( $""" @@ -378,8 +270,9 @@ LifeCycleClassInitializeAndCleanupBeforeEachDerivedClass.TestCleanup was called + GenerateTraceDebugPrefixedMessage("LifeCycleClassInitializeAndCleanupNone.Dispose was called") : GenerateTraceDebugPrefixedMessage("LifeCycleClassInitializeAndCleanupNone.Dispose was called"))} - """); - caseClassInitializeAndCleanupNone.Messages[2].Text.Should().Be( + """, + caseClassInitializeAndCleanupNone.Messages[1].Text); + Assert.AreEqual( $""" @@ -393,12 +286,13 @@ LifeCycleClassInitializeAndCleanupNone.TestCleanup was called ? "LifeCycleClassInitializeAndCleanupNone.DisposeAsync was called\r\nLifeCycleClassInitializeAndCleanupNone.Dispose was called" : "LifeCycleClassInitializeAndCleanupNone.Dispose was called")} - """); + """, + caseClassInitializeAndCleanupNone.Messages[2].Text); - Microsoft.VisualStudio.TestPlatform.ObjectModel.TestResult caseClassInitializeBeforeEachDerivedClassAndClassCleanupNone = RunEventsHandler.PassedTests.Single(x => x.TestCase.FullyQualifiedName.Contains("LifeCycleClassInitializeBeforeEachDerivedClassAndClassCleanupNone")); - caseClassInitializeBeforeEachDerivedClassAndClassCleanupNone.Outcome.Should().Be(Microsoft.VisualStudio.TestPlatform.ObjectModel.TestOutcome.Passed); - caseClassInitializeBeforeEachDerivedClassAndClassCleanupNone.Messages.Should().HaveCount(3); - caseClassInitializeBeforeEachDerivedClassAndClassCleanupNone.Messages[0].Text.Should().Be( + TestResult caseClassInitializeBeforeEachDerivedClassAndClassCleanupNone = RunEventsHandler.PassedTests.Single(x => x.TestCase.FullyQualifiedName.Contains("LifeCycleClassInitializeBeforeEachDerivedClassAndClassCleanupNone")); + Assert.AreEqual(TestOutcome.Passed, caseClassInitializeBeforeEachDerivedClassAndClassCleanupNone.Outcome); + Assert.HasCount(3, caseClassInitializeBeforeEachDerivedClassAndClassCleanupNone.Messages); + Assert.AreEqual( $""" Console: LifeCycleClassInitializeBeforeEachDerivedClassAndClassCleanupNone.ClassInitialize was called Console: LifeCycleClassInitializeBeforeEachDerivedClassAndClassCleanupNone.ctor was called @@ -409,8 +303,9 @@ LifeCycleClassInitializeAndCleanupNone.TestCleanup was called ? "Console: LifeCycleClassInitializeBeforeEachDerivedClassAndClassCleanupNone.DisposeAsync was called\r\nConsole: LifeCycleClassInitializeBeforeEachDerivedClassAndClassCleanupNone.Dispose was called" : "Console: LifeCycleClassInitializeBeforeEachDerivedClassAndClassCleanupNone.Dispose was called")} - """); - caseClassInitializeBeforeEachDerivedClassAndClassCleanupNone.Messages[1].Text.Should().Be( + """, + caseClassInitializeBeforeEachDerivedClassAndClassCleanupNone.Messages[0].Text); + Assert.AreEqual( $""" @@ -426,8 +321,9 @@ LifeCycleClassInitializeAndCleanupNone.TestCleanup was called + GenerateTraceDebugPrefixedMessage("LifeCycleClassInitializeBeforeEachDerivedClassAndClassCleanupNone.Dispose was called") : GenerateTraceDebugPrefixedMessage("LifeCycleClassInitializeBeforeEachDerivedClassAndClassCleanupNone.Dispose was called"))} - """); - caseClassInitializeBeforeEachDerivedClassAndClassCleanupNone.Messages[2].Text.Should().Be( + """, + caseClassInitializeBeforeEachDerivedClassAndClassCleanupNone.Messages[1].Text); + Assert.AreEqual( $""" @@ -441,12 +337,13 @@ LifeCycleClassInitializeBeforeEachDerivedClassAndClassCleanupNone.TestCleanup wa ? "LifeCycleClassInitializeBeforeEachDerivedClassAndClassCleanupNone.DisposeAsync was called\r\nLifeCycleClassInitializeBeforeEachDerivedClassAndClassCleanupNone.Dispose was called" : "LifeCycleClassInitializeBeforeEachDerivedClassAndClassCleanupNone.Dispose was called")} - """); + """, + caseClassInitializeBeforeEachDerivedClassAndClassCleanupNone.Messages[2].Text); - Microsoft.VisualStudio.TestPlatform.ObjectModel.TestResult caseClassInitializeNoneAndClassCleanupBeforeEachDerivedClass = RunEventsHandler.PassedTests.Single(x => x.TestCase.FullyQualifiedName.Contains("LifeCycleClassInitializeNoneAndClassCleanupBeforeEachDerivedClass")); - caseClassInitializeNoneAndClassCleanupBeforeEachDerivedClass.Outcome.Should().Be(Microsoft.VisualStudio.TestPlatform.ObjectModel.TestOutcome.Passed); - caseClassInitializeNoneAndClassCleanupBeforeEachDerivedClass.Messages.Should().HaveCount(3); - caseClassInitializeNoneAndClassCleanupBeforeEachDerivedClass.Messages[0].Text.Should().Be( + TestResult caseClassInitializeNoneAndClassCleanupBeforeEachDerivedClass = RunEventsHandler.PassedTests.Single(x => x.TestCase.FullyQualifiedName.Contains("LifeCycleClassInitializeNoneAndClassCleanupBeforeEachDerivedClass")); + Assert.AreEqual(TestOutcome.Passed, caseClassInitializeNoneAndClassCleanupBeforeEachDerivedClass.Outcome); + Assert.HasCount(3, caseClassInitializeNoneAndClassCleanupBeforeEachDerivedClass.Messages); + Assert.AreEqual( $""" Console: LifeCycleClassInitializeNoneAndClassCleanupBeforeEachDerivedClass.ClassInitialize was called Console: LifeCycleClassInitializeNoneAndClassCleanupBeforeEachDerivedClass.ctor was called @@ -457,8 +354,9 @@ LifeCycleClassInitializeBeforeEachDerivedClassAndClassCleanupNone.TestCleanup wa ? "Console: LifeCycleClassInitializeNoneAndClassCleanupBeforeEachDerivedClass.DisposeAsync was called\r\nConsole: LifeCycleClassInitializeNoneAndClassCleanupBeforeEachDerivedClass.Dispose was called" : "Console: LifeCycleClassInitializeNoneAndClassCleanupBeforeEachDerivedClass.Dispose was called")} - """); - caseClassInitializeNoneAndClassCleanupBeforeEachDerivedClass.Messages[1].Text.Should().Be( + """, + caseClassInitializeNoneAndClassCleanupBeforeEachDerivedClass.Messages[0].Text); + Assert.AreEqual( $""" @@ -474,8 +372,9 @@ LifeCycleClassInitializeBeforeEachDerivedClassAndClassCleanupNone.TestCleanup wa + GenerateTraceDebugPrefixedMessage("LifeCycleClassInitializeNoneAndClassCleanupBeforeEachDerivedClass.Dispose was called") : GenerateTraceDebugPrefixedMessage("LifeCycleClassInitializeNoneAndClassCleanupBeforeEachDerivedClass.Dispose was called"))} - """); - caseClassInitializeNoneAndClassCleanupBeforeEachDerivedClass.Messages[2].Text.Should().Be( + """, + caseClassInitializeNoneAndClassCleanupBeforeEachDerivedClass.Messages[1].Text); + Assert.AreEqual( $""" @@ -489,12 +388,13 @@ LifeCycleClassInitializeNoneAndClassCleanupBeforeEachDerivedClass.TestCleanup wa ? "LifeCycleClassInitializeNoneAndClassCleanupBeforeEachDerivedClass.DisposeAsync was called\r\nLifeCycleClassInitializeNoneAndClassCleanupBeforeEachDerivedClass.Dispose was called" : "LifeCycleClassInitializeNoneAndClassCleanupBeforeEachDerivedClass.Dispose was called")} - """); + """, + caseClassInitializeNoneAndClassCleanupBeforeEachDerivedClass.Messages[2].Text); - Microsoft.VisualStudio.TestPlatform.ObjectModel.TestResult caseDerivedClassInitializeAndCleanupBeforeEachDerivedClass = RunEventsHandler.PassedTests.Single(x => x.TestCase.FullyQualifiedName.Contains("LifeCycleDerivedClassInitializeAndCleanupBeforeEachDerivedClass.DerivedClassTestMethod")); - caseDerivedClassInitializeAndCleanupBeforeEachDerivedClass.Outcome.Should().Be(Microsoft.VisualStudio.TestPlatform.ObjectModel.TestOutcome.Passed); - caseDerivedClassInitializeAndCleanupBeforeEachDerivedClass.Messages.Should().HaveCount(3); - caseDerivedClassInitializeAndCleanupBeforeEachDerivedClass.Messages[0].Text.Should().Be( + TestResult caseDerivedClassInitializeAndCleanupBeforeEachDerivedClass = RunEventsHandler.PassedTests.Single(x => x.TestCase.FullyQualifiedName.Contains("LifeCycleDerivedClassInitializeAndCleanupBeforeEachDerivedClass.DerivedClassTestMethod")); + Assert.AreEqual(TestOutcome.Passed, caseDerivedClassInitializeAndCleanupBeforeEachDerivedClass.Outcome); + Assert.HasCount(3, caseDerivedClassInitializeAndCleanupBeforeEachDerivedClass.Messages); + Assert.AreEqual( $""" Console: LifeCycleClassInitializeAndCleanupBeforeEachDerivedClass.ClassInitialize was called Console: LifeCycleDerivedClassInitializeAndCleanupBeforeEachDerivedClass.ClassInitialize was called @@ -509,8 +409,9 @@ LifeCycleClassInitializeNoneAndClassCleanupBeforeEachDerivedClass.TestCleanup wa ? "Console: LifeCycleClassInitializeAndCleanupBeforeEachDerivedClass.DisposeAsync was called\r\nConsole: LifeCycleClassInitializeAndCleanupBeforeEachDerivedClass.Dispose was called" : "Console: LifeCycleClassInitializeAndCleanupBeforeEachDerivedClass.Dispose was called")} - """); - caseDerivedClassInitializeAndCleanupBeforeEachDerivedClass.Messages[1].Text.Should().Be( + """, + caseDerivedClassInitializeAndCleanupBeforeEachDerivedClass.Messages[0].Text); + Assert.AreEqual( $""" @@ -530,8 +431,9 @@ LifeCycleClassInitializeNoneAndClassCleanupBeforeEachDerivedClass.TestCleanup wa + GenerateTraceDebugPrefixedMessage("LifeCycleClassInitializeAndCleanupBeforeEachDerivedClass.Dispose was called") : GenerateTraceDebugPrefixedMessage("LifeCycleClassInitializeAndCleanupBeforeEachDerivedClass.Dispose was called"))} - """); - caseDerivedClassInitializeAndCleanupBeforeEachDerivedClass.Messages[2].Text.Should().Be( + """, + caseDerivedClassInitializeAndCleanupBeforeEachDerivedClass.Messages[1].Text); + Assert.AreEqual( $""" @@ -549,13 +451,14 @@ LifeCycleClassInitializeAndCleanupBeforeEachDerivedClass.TestCleanup was called ? "LifeCycleClassInitializeAndCleanupBeforeEachDerivedClass.DisposeAsync was called\r\nLifeCycleClassInitializeAndCleanupBeforeEachDerivedClass.Dispose was called" : "LifeCycleClassInitializeAndCleanupBeforeEachDerivedClass.Dispose was called")} - """); + """, + caseDerivedClassInitializeAndCleanupBeforeEachDerivedClass.Messages[2].Text); // Test the parent test method. - Microsoft.VisualStudio.TestPlatform.ObjectModel.TestResult caseDerivedClassInitializeAndCleanupBeforeEachDerivedClassParentTestMethod = RunEventsHandler.PassedTests.Single(x => x.TestCase.FullyQualifiedName.Contains("LifeCycleDerivedClassInitializeAndCleanupBeforeEachDerivedClass.TestMethod")); - caseDerivedClassInitializeAndCleanupBeforeEachDerivedClassParentTestMethod.Outcome.Should().Be(Microsoft.VisualStudio.TestPlatform.ObjectModel.TestOutcome.Passed); - caseDerivedClassInitializeAndCleanupBeforeEachDerivedClassParentTestMethod.Messages.Should().HaveCount(3); - caseDerivedClassInitializeAndCleanupBeforeEachDerivedClassParentTestMethod.Messages[0].Text.Should().Be( + TestResult caseDerivedClassInitializeAndCleanupBeforeEachDerivedClassParentTestMethod = RunEventsHandler.PassedTests.Single(x => x.TestCase.FullyQualifiedName.Contains("LifeCycleDerivedClassInitializeAndCleanupBeforeEachDerivedClass.TestMethod")); + Assert.AreEqual(TestOutcome.Passed, caseDerivedClassInitializeAndCleanupBeforeEachDerivedClassParentTestMethod.Outcome); + Assert.HasCount(3, caseDerivedClassInitializeAndCleanupBeforeEachDerivedClassParentTestMethod.Messages); + Assert.AreEqual( $""" Console: LifeCycleClassInitializeAndCleanupBeforeEachDerivedClass.ctor was called Console: LifeCycleDerivedClassInitializeAndCleanupBeforeEachDerivedClass.ctor was called @@ -568,8 +471,9 @@ LifeCycleClassInitializeAndCleanupBeforeEachDerivedClass.TestCleanup was called ? "Console: LifeCycleClassInitializeAndCleanupBeforeEachDerivedClass.DisposeAsync was called\r\nConsole: LifeCycleClassInitializeAndCleanupBeforeEachDerivedClass.Dispose was called" : "Console: LifeCycleClassInitializeAndCleanupBeforeEachDerivedClass.Dispose was called")} - """); - caseDerivedClassInitializeAndCleanupBeforeEachDerivedClassParentTestMethod.Messages[1].Text.Should().Be( + """, + caseDerivedClassInitializeAndCleanupBeforeEachDerivedClassParentTestMethod.Messages[0].Text); + Assert.AreEqual( $""" @@ -587,8 +491,9 @@ LifeCycleClassInitializeAndCleanupBeforeEachDerivedClass.TestCleanup was called + GenerateTraceDebugPrefixedMessage("LifeCycleClassInitializeAndCleanupBeforeEachDerivedClass.Dispose was called") : GenerateTraceDebugPrefixedMessage("LifeCycleClassInitializeAndCleanupBeforeEachDerivedClass.Dispose was called"))} - """); - caseDerivedClassInitializeAndCleanupBeforeEachDerivedClassParentTestMethod.Messages[2].Text.Should().Be( + """, + caseDerivedClassInitializeAndCleanupBeforeEachDerivedClassParentTestMethod.Messages[1].Text); + Assert.AreEqual( $""" @@ -604,12 +509,13 @@ LifeCycleClassInitializeAndCleanupBeforeEachDerivedClass.TestCleanup was called ? "LifeCycleClassInitializeAndCleanupBeforeEachDerivedClass.DisposeAsync was called\r\nLifeCycleClassInitializeAndCleanupBeforeEachDerivedClass.Dispose was called" : "LifeCycleClassInitializeAndCleanupBeforeEachDerivedClass.Dispose was called")} - """); + """, + caseDerivedClassInitializeAndCleanupBeforeEachDerivedClassParentTestMethod.Messages[2].Text); - Microsoft.VisualStudio.TestPlatform.ObjectModel.TestResult caseDerivedClassInitializeAndCleanupNone = RunEventsHandler.PassedTests.Single(x => x.TestCase.FullyQualifiedName.Contains("LifeCycleDerivedClassInitializeAndCleanupNone.DerivedClassTestMethod")); - caseDerivedClassInitializeAndCleanupNone.Outcome.Should().Be(Microsoft.VisualStudio.TestPlatform.ObjectModel.TestOutcome.Passed); - caseDerivedClassInitializeAndCleanupNone.Messages.Should().HaveCount(3); - caseDerivedClassInitializeAndCleanupNone.Messages[0].Text.Should().Be( + TestResult caseDerivedClassInitializeAndCleanupNone = RunEventsHandler.PassedTests.Single(x => x.TestCase.FullyQualifiedName.Contains("LifeCycleDerivedClassInitializeAndCleanupNone.DerivedClassTestMethod")); + Assert.AreEqual(TestOutcome.Passed, caseDerivedClassInitializeAndCleanupNone.Outcome); + Assert.HasCount(3, caseDerivedClassInitializeAndCleanupNone.Messages); + Assert.AreEqual( $""" Console: LifeCycleDerivedClassInitializeAndCleanupNone.ClassInitialize was called Console: LifeCycleClassInitializeAndCleanupNone.ctor was called @@ -623,8 +529,9 @@ LifeCycleClassInitializeAndCleanupBeforeEachDerivedClass.TestCleanup was called ? "Console: LifeCycleClassInitializeAndCleanupNone.DisposeAsync was called\r\nConsole: LifeCycleClassInitializeAndCleanupNone.Dispose was called" : "Console: LifeCycleClassInitializeAndCleanupNone.Dispose was called")} - """); - caseDerivedClassInitializeAndCleanupNone.Messages[1].Text.Should().Be( + """, + caseDerivedClassInitializeAndCleanupNone.Messages[0].Text); + Assert.AreEqual( $""" @@ -643,8 +550,9 @@ LifeCycleClassInitializeAndCleanupBeforeEachDerivedClass.TestCleanup was called + GenerateTraceDebugPrefixedMessage("LifeCycleClassInitializeAndCleanupNone.Dispose was called") : GenerateTraceDebugPrefixedMessage("LifeCycleClassInitializeAndCleanupNone.Dispose was called"))} - """); - caseDerivedClassInitializeAndCleanupNone.Messages[2].Text.Should().Be( + """, + caseDerivedClassInitializeAndCleanupNone.Messages[1].Text); + Assert.AreEqual( $""" @@ -661,13 +569,14 @@ LifeCycleClassInitializeAndCleanupNone.TestCleanup was called ? "LifeCycleClassInitializeAndCleanupNone.DisposeAsync was called\r\nLifeCycleClassInitializeAndCleanupNone.Dispose was called" : "LifeCycleClassInitializeAndCleanupNone.Dispose was called")} - """); + """, + caseDerivedClassInitializeAndCleanupNone.Messages[2].Text); // Test the parent test method. - Microsoft.VisualStudio.TestPlatform.ObjectModel.TestResult caseDerivedClassInitializeAndCleanupNoneParentTestMethod = RunEventsHandler.PassedTests.Single(x => x.TestCase.FullyQualifiedName.Contains("LifeCycleDerivedClassInitializeAndCleanupNone.TestMethod")); - caseDerivedClassInitializeAndCleanupNoneParentTestMethod.Outcome.Should().Be(Microsoft.VisualStudio.TestPlatform.ObjectModel.TestOutcome.Passed); - caseDerivedClassInitializeAndCleanupNoneParentTestMethod.Messages.Should().HaveCount(3); - caseDerivedClassInitializeAndCleanupNoneParentTestMethod.Messages[0].Text.Should().Be( + TestResult caseDerivedClassInitializeAndCleanupNoneParentTestMethod = RunEventsHandler.PassedTests.Single(x => x.TestCase.FullyQualifiedName.Contains("LifeCycleDerivedClassInitializeAndCleanupNone.TestMethod")); + Assert.AreEqual(TestOutcome.Passed, caseDerivedClassInitializeAndCleanupNoneParentTestMethod.Outcome); + Assert.HasCount(3, caseDerivedClassInitializeAndCleanupNoneParentTestMethod.Messages); + Assert.AreEqual( $""" Console: LifeCycleClassInitializeAndCleanupNone.ctor was called Console: LifeCycleDerivedClassInitializeAndCleanupNone.ctor was called @@ -680,8 +589,9 @@ LifeCycleClassInitializeAndCleanupNone.TestCleanup was called ? "Console: LifeCycleClassInitializeAndCleanupNone.DisposeAsync was called\r\nConsole: LifeCycleClassInitializeAndCleanupNone.Dispose was called" : "Console: LifeCycleClassInitializeAndCleanupNone.Dispose was called")} - """); - caseDerivedClassInitializeAndCleanupNoneParentTestMethod.Messages[1].Text.Should().Be( + """, + caseDerivedClassInitializeAndCleanupNoneParentTestMethod.Messages[0].Text); + Assert.AreEqual( $""" @@ -699,8 +609,9 @@ LifeCycleClassInitializeAndCleanupNone.TestCleanup was called + GenerateTraceDebugPrefixedMessage("LifeCycleClassInitializeAndCleanupNone.Dispose was called") : GenerateTraceDebugPrefixedMessage("LifeCycleClassInitializeAndCleanupNone.Dispose was called"))} - """); - caseDerivedClassInitializeAndCleanupNoneParentTestMethod.Messages[2].Text.Should().Be( + """, + caseDerivedClassInitializeAndCleanupNoneParentTestMethod.Messages[1].Text); + Assert.AreEqual( $""" @@ -716,12 +627,13 @@ LifeCycleClassInitializeAndCleanupNone.TestCleanup was called ? "LifeCycleClassInitializeAndCleanupNone.DisposeAsync was called\r\nLifeCycleClassInitializeAndCleanupNone.Dispose was called" : "LifeCycleClassInitializeAndCleanupNone.Dispose was called")} - """); + """, + caseDerivedClassInitializeAndCleanupNoneParentTestMethod.Messages[2].Text); - Microsoft.VisualStudio.TestPlatform.ObjectModel.TestResult caseDerivedClassInitializeBeforeEachDerivedClassAndClassCleanupNone = RunEventsHandler.PassedTests.Single(x => x.TestCase.FullyQualifiedName.Contains("LifeCycleDerivedClassInitializeBeforeEachDerivedClassAndClassCleanupNone.DerivedClassTestMethod")); - caseDerivedClassInitializeBeforeEachDerivedClassAndClassCleanupNone.Outcome.Should().Be(Microsoft.VisualStudio.TestPlatform.ObjectModel.TestOutcome.Passed); - caseDerivedClassInitializeBeforeEachDerivedClassAndClassCleanupNone.Messages.Should().HaveCount(3); - caseDerivedClassInitializeBeforeEachDerivedClassAndClassCleanupNone.Messages[0].Text.Should().Be( + TestResult caseDerivedClassInitializeBeforeEachDerivedClassAndClassCleanupNone = RunEventsHandler.PassedTests.Single(x => x.TestCase.FullyQualifiedName.Contains("LifeCycleDerivedClassInitializeBeforeEachDerivedClassAndClassCleanupNone.DerivedClassTestMethod")); + Assert.AreEqual(TestOutcome.Passed, caseDerivedClassInitializeBeforeEachDerivedClassAndClassCleanupNone.Outcome); + Assert.HasCount(3, caseDerivedClassInitializeBeforeEachDerivedClassAndClassCleanupNone.Messages); + Assert.AreEqual( $""" Console: LifeCycleClassInitializeBeforeEachDerivedClassAndClassCleanupNone.ClassInitialize was called Console: LifeCycleDerivedClassInitializeBeforeEachDerivedClassAndClassCleanupNone.ClassInitialize was called @@ -736,8 +648,9 @@ LifeCycleClassInitializeAndCleanupNone.TestCleanup was called ? "Console: LifeCycleClassInitializeBeforeEachDerivedClassAndClassCleanupNone.DisposeAsync was called\r\nConsole: LifeCycleClassInitializeBeforeEachDerivedClassAndClassCleanupNone.Dispose was called" : "Console: LifeCycleClassInitializeBeforeEachDerivedClassAndClassCleanupNone.Dispose was called")} - """); - caseDerivedClassInitializeBeforeEachDerivedClassAndClassCleanupNone.Messages[1].Text.Should().Be( + """, + caseDerivedClassInitializeBeforeEachDerivedClassAndClassCleanupNone.Messages[0].Text); + Assert.AreEqual( $""" @@ -757,8 +670,9 @@ LifeCycleClassInitializeAndCleanupNone.TestCleanup was called + GenerateTraceDebugPrefixedMessage("LifeCycleClassInitializeBeforeEachDerivedClassAndClassCleanupNone.Dispose was called") : GenerateTraceDebugPrefixedMessage("LifeCycleClassInitializeBeforeEachDerivedClassAndClassCleanupNone.Dispose was called"))} - """); - caseDerivedClassInitializeBeforeEachDerivedClassAndClassCleanupNone.Messages[2].Text.Should().Be( + """, + caseDerivedClassInitializeBeforeEachDerivedClassAndClassCleanupNone.Messages[1].Text); + Assert.AreEqual( $""" @@ -776,13 +690,14 @@ LifeCycleClassInitializeBeforeEachDerivedClassAndClassCleanupNone.TestCleanup wa ? "LifeCycleClassInitializeBeforeEachDerivedClassAndClassCleanupNone.DisposeAsync was called\r\nLifeCycleClassInitializeBeforeEachDerivedClassAndClassCleanupNone.Dispose was called" : "LifeCycleClassInitializeBeforeEachDerivedClassAndClassCleanupNone.Dispose was called")} - """); + """, + caseDerivedClassInitializeBeforeEachDerivedClassAndClassCleanupNone.Messages[2].Text); // Test the parent test method. - Microsoft.VisualStudio.TestPlatform.ObjectModel.TestResult caseDerivedClassInitializeBeforeEachDerivedClassAndClassCleanupNoneParentTestMethod = RunEventsHandler.PassedTests.Single(x => x.TestCase.FullyQualifiedName.Contains("LifeCycleDerivedClassInitializeBeforeEachDerivedClassAndClassCleanupNone.TestMethod")); - caseDerivedClassInitializeBeforeEachDerivedClassAndClassCleanupNoneParentTestMethod.Outcome.Should().Be(Microsoft.VisualStudio.TestPlatform.ObjectModel.TestOutcome.Passed); - caseDerivedClassInitializeBeforeEachDerivedClassAndClassCleanupNoneParentTestMethod.Messages.Should().HaveCount(3); - caseDerivedClassInitializeBeforeEachDerivedClassAndClassCleanupNoneParentTestMethod.Messages[0].Text.Should().Be( + TestResult caseDerivedClassInitializeBeforeEachDerivedClassAndClassCleanupNoneParentTestMethod = RunEventsHandler.PassedTests.Single(x => x.TestCase.FullyQualifiedName.Contains("LifeCycleDerivedClassInitializeBeforeEachDerivedClassAndClassCleanupNone.TestMethod")); + Assert.AreEqual(TestOutcome.Passed, caseDerivedClassInitializeBeforeEachDerivedClassAndClassCleanupNoneParentTestMethod.Outcome); + Assert.HasCount(3, caseDerivedClassInitializeBeforeEachDerivedClassAndClassCleanupNoneParentTestMethod.Messages); + Assert.AreEqual( $""" Console: LifeCycleClassInitializeBeforeEachDerivedClassAndClassCleanupNone.ctor was called Console: LifeCycleDerivedClassInitializeBeforeEachDerivedClassAndClassCleanupNone.ctor was called @@ -795,8 +710,9 @@ LifeCycleClassInitializeBeforeEachDerivedClassAndClassCleanupNone.TestCleanup wa ? "Console: LifeCycleClassInitializeBeforeEachDerivedClassAndClassCleanupNone.DisposeAsync was called\r\nConsole: LifeCycleClassInitializeBeforeEachDerivedClassAndClassCleanupNone.Dispose was called" : "Console: LifeCycleClassInitializeBeforeEachDerivedClassAndClassCleanupNone.Dispose was called")} - """); - caseDerivedClassInitializeBeforeEachDerivedClassAndClassCleanupNoneParentTestMethod.Messages[1].Text.Should().Be( + """, + caseDerivedClassInitializeBeforeEachDerivedClassAndClassCleanupNoneParentTestMethod.Messages[0].Text); + Assert.AreEqual( $""" @@ -814,8 +730,9 @@ LifeCycleClassInitializeBeforeEachDerivedClassAndClassCleanupNone.TestCleanup wa + GenerateTraceDebugPrefixedMessage("LifeCycleClassInitializeBeforeEachDerivedClassAndClassCleanupNone.Dispose was called") : GenerateTraceDebugPrefixedMessage("LifeCycleClassInitializeBeforeEachDerivedClassAndClassCleanupNone.Dispose was called"))} - """); - caseDerivedClassInitializeBeforeEachDerivedClassAndClassCleanupNoneParentTestMethod.Messages[2].Text.Should().Be( + """, + caseDerivedClassInitializeBeforeEachDerivedClassAndClassCleanupNoneParentTestMethod.Messages[1].Text); + Assert.AreEqual( $""" @@ -831,12 +748,13 @@ LifeCycleClassInitializeBeforeEachDerivedClassAndClassCleanupNone.TestCleanup wa ? "LifeCycleClassInitializeBeforeEachDerivedClassAndClassCleanupNone.DisposeAsync was called\r\nLifeCycleClassInitializeBeforeEachDerivedClassAndClassCleanupNone.Dispose was called" : "LifeCycleClassInitializeBeforeEachDerivedClassAndClassCleanupNone.Dispose was called")} - """); + """, + caseDerivedClassInitializeBeforeEachDerivedClassAndClassCleanupNoneParentTestMethod.Messages[2].Text); - Microsoft.VisualStudio.TestPlatform.ObjectModel.TestResult caseDerivedClassInitializeNoneAndClassCleanupBeforeEachDerivedClass = RunEventsHandler.PassedTests.Single(x => x.TestCase.FullyQualifiedName.Contains("LifeCycleDerivedClassInitializeNoneAndClassCleanupBeforeEachDerivedClass.DerivedClassTestMethod")); - caseDerivedClassInitializeNoneAndClassCleanupBeforeEachDerivedClass.Outcome.Should().Be(Microsoft.VisualStudio.TestPlatform.ObjectModel.TestOutcome.Passed); - caseDerivedClassInitializeNoneAndClassCleanupBeforeEachDerivedClass.Messages.Should().HaveCount(3); - caseDerivedClassInitializeNoneAndClassCleanupBeforeEachDerivedClass.Messages[0].Text.Should().Be( + TestResult caseDerivedClassInitializeNoneAndClassCleanupBeforeEachDerivedClass = RunEventsHandler.PassedTests.Single(x => x.TestCase.FullyQualifiedName.Contains("LifeCycleDerivedClassInitializeNoneAndClassCleanupBeforeEachDerivedClass.DerivedClassTestMethod")); + Assert.AreEqual(TestOutcome.Passed, caseDerivedClassInitializeNoneAndClassCleanupBeforeEachDerivedClass.Outcome); + Assert.HasCount(3, caseDerivedClassInitializeNoneAndClassCleanupBeforeEachDerivedClass.Messages); + Assert.AreEqual( $""" Console: LifeCycleDerivedClassInitializeNoneAndClassCleanupBeforeEachDerivedClass.ClassInitialize was called Console: LifeCycleClassInitializeNoneAndClassCleanupBeforeEachDerivedClass.ctor was called @@ -850,8 +768,9 @@ LifeCycleClassInitializeBeforeEachDerivedClassAndClassCleanupNone.TestCleanup wa ? "Console: LifeCycleClassInitializeNoneAndClassCleanupBeforeEachDerivedClass.DisposeAsync was called\r\nConsole: LifeCycleClassInitializeNoneAndClassCleanupBeforeEachDerivedClass.Dispose was called" : "Console: LifeCycleClassInitializeNoneAndClassCleanupBeforeEachDerivedClass.Dispose was called")} - """); - caseDerivedClassInitializeNoneAndClassCleanupBeforeEachDerivedClass.Messages[1].Text.Should().Be( + """, + caseDerivedClassInitializeNoneAndClassCleanupBeforeEachDerivedClass.Messages[0].Text); + Assert.AreEqual( $""" @@ -870,8 +789,9 @@ LifeCycleClassInitializeBeforeEachDerivedClassAndClassCleanupNone.TestCleanup wa + GenerateTraceDebugPrefixedMessage("LifeCycleClassInitializeNoneAndClassCleanupBeforeEachDerivedClass.Dispose was called") : GenerateTraceDebugPrefixedMessage("LifeCycleClassInitializeNoneAndClassCleanupBeforeEachDerivedClass.Dispose was called"))} - """); - caseDerivedClassInitializeNoneAndClassCleanupBeforeEachDerivedClass.Messages[2].Text.Should().Be( + """, + caseDerivedClassInitializeNoneAndClassCleanupBeforeEachDerivedClass.Messages[1].Text); + Assert.AreEqual( $""" @@ -888,12 +808,13 @@ LifeCycleClassInitializeNoneAndClassCleanupBeforeEachDerivedClass.TestCleanup wa ? "LifeCycleClassInitializeNoneAndClassCleanupBeforeEachDerivedClass.DisposeAsync was called\r\nLifeCycleClassInitializeNoneAndClassCleanupBeforeEachDerivedClass.Dispose was called" : "LifeCycleClassInitializeNoneAndClassCleanupBeforeEachDerivedClass.Dispose was called")} - """); + """, + caseDerivedClassInitializeNoneAndClassCleanupBeforeEachDerivedClass.Messages[2].Text); - Microsoft.VisualStudio.TestPlatform.ObjectModel.TestResult caseClassCleanupEndOfAssemblyAndBeforeEachDerivedClass = RunEventsHandler.PassedTests.Single(x => x.TestCase.FullyQualifiedName.Contains("LifeCycleClassCleanupEndOfAssemblyAndBeforeEachDerivedClass.TestMethod")); - caseClassCleanupEndOfAssemblyAndBeforeEachDerivedClass.Outcome.Should().Be(Microsoft.VisualStudio.TestPlatform.ObjectModel.TestOutcome.Passed); - caseClassCleanupEndOfAssemblyAndBeforeEachDerivedClass.Messages.Should().HaveCount(3); - caseClassCleanupEndOfAssemblyAndBeforeEachDerivedClass.Messages[0].Text.Should().Be( + TestResult caseClassCleanupEndOfAssemblyAndBeforeEachDerivedClass = RunEventsHandler.PassedTests.Single(x => x.TestCase.FullyQualifiedName.Contains("LifeCycleClassCleanupEndOfAssemblyAndBeforeEachDerivedClass.TestMethod")); + Assert.AreEqual(TestOutcome.Passed, caseClassCleanupEndOfAssemblyAndBeforeEachDerivedClass.Outcome); + Assert.HasCount(3, caseClassCleanupEndOfAssemblyAndBeforeEachDerivedClass.Messages); + Assert.AreEqual( $""" Console: LifeCycleClassCleanupEndOfAssemblyAndBeforeEachDerivedClass.ClassInitialize was called Console: LifeCycleClassCleanupEndOfAssemblyAndBeforeEachDerivedClass.ctor was called @@ -904,8 +825,9 @@ LifeCycleClassInitializeNoneAndClassCleanupBeforeEachDerivedClass.TestCleanup wa ? "Console: LifeCycleClassCleanupEndOfAssemblyAndBeforeEachDerivedClass.DisposeAsync was called\r\nConsole: LifeCycleClassCleanupEndOfAssemblyAndBeforeEachDerivedClass.Dispose was called" : "Console: LifeCycleClassCleanupEndOfAssemblyAndBeforeEachDerivedClass.Dispose was called")} - """); - caseClassCleanupEndOfAssemblyAndBeforeEachDerivedClass.Messages[1].Text.Should().Be( + """, + caseClassCleanupEndOfAssemblyAndBeforeEachDerivedClass.Messages[0].Text); + Assert.AreEqual( $""" @@ -921,8 +843,9 @@ LifeCycleClassInitializeNoneAndClassCleanupBeforeEachDerivedClass.TestCleanup wa + GenerateTraceDebugPrefixedMessage("LifeCycleClassCleanupEndOfAssemblyAndBeforeEachDerivedClass.Dispose was called") : GenerateTraceDebugPrefixedMessage("LifeCycleClassCleanupEndOfAssemblyAndBeforeEachDerivedClass.Dispose was called"))} - """); - caseClassCleanupEndOfAssemblyAndBeforeEachDerivedClass.Messages[2].Text.Should().Be( + """, + caseClassCleanupEndOfAssemblyAndBeforeEachDerivedClass.Messages[1].Text); + Assert.AreEqual( $""" @@ -936,12 +859,13 @@ LifeCycleClassCleanupEndOfAssemblyAndBeforeEachDerivedClass.TestCleanup was call ? "LifeCycleClassCleanupEndOfAssemblyAndBeforeEachDerivedClass.DisposeAsync was called\r\nLifeCycleClassCleanupEndOfAssemblyAndBeforeEachDerivedClass.Dispose was called" : "LifeCycleClassCleanupEndOfAssemblyAndBeforeEachDerivedClass.Dispose was called")} - """); + """, + caseClassCleanupEndOfAssemblyAndBeforeEachDerivedClass.Messages[2].Text); - Microsoft.VisualStudio.TestPlatform.ObjectModel.TestResult caseClassCleanupEndOfAssemblyAndNone = RunEventsHandler.PassedTests.Single(x => x.TestCase.FullyQualifiedName.Contains("LifeCycleClassCleanupEndOfAssemblyAndNone.TestMethod")); - caseClassCleanupEndOfAssemblyAndNone.Outcome.Should().Be(Microsoft.VisualStudio.TestPlatform.ObjectModel.TestOutcome.Passed); - caseClassCleanupEndOfAssemblyAndNone.Messages.Should().HaveCount(3); - caseClassCleanupEndOfAssemblyAndNone.Messages[0].Text.Should().Be( + TestResult caseClassCleanupEndOfAssemblyAndNone = RunEventsHandler.PassedTests.Single(x => x.TestCase.FullyQualifiedName.Contains("LifeCycleClassCleanupEndOfAssemblyAndNone.TestMethod")); + Assert.AreEqual(TestOutcome.Passed, caseClassCleanupEndOfAssemblyAndNone.Outcome); + Assert.HasCount(3, caseClassCleanupEndOfAssemblyAndNone.Messages); + Assert.AreEqual( $""" Console: LifeCycleClassCleanupEndOfAssemblyAndNone.ClassInitialize was called Console: LifeCycleClassCleanupEndOfAssemblyAndNone.ctor was called @@ -952,8 +876,9 @@ LifeCycleClassCleanupEndOfAssemblyAndBeforeEachDerivedClass.TestCleanup was call ? "Console: LifeCycleClassCleanupEndOfAssemblyAndNone.DisposeAsync was called\r\nConsole: LifeCycleClassCleanupEndOfAssemblyAndNone.Dispose was called" : "Console: LifeCycleClassCleanupEndOfAssemblyAndNone.Dispose was called")} - """); - caseClassCleanupEndOfAssemblyAndNone.Messages[1].Text.Should().Be( + """, + caseClassCleanupEndOfAssemblyAndNone.Messages[0].Text); + Assert.AreEqual( $""" @@ -969,8 +894,9 @@ LifeCycleClassCleanupEndOfAssemblyAndBeforeEachDerivedClass.TestCleanup was call + GenerateTraceDebugPrefixedMessage("LifeCycleClassCleanupEndOfAssemblyAndNone.Dispose was called") : GenerateTraceDebugPrefixedMessage("LifeCycleClassCleanupEndOfAssemblyAndNone.Dispose was called"))} - """); - caseClassCleanupEndOfAssemblyAndNone.Messages[2].Text.Should().Be( + """, + caseClassCleanupEndOfAssemblyAndNone.Messages[1].Text); + Assert.AreEqual( $""" @@ -984,12 +910,13 @@ LifeCycleClassCleanupEndOfAssemblyAndNone.TestCleanup was called ? "LifeCycleClassCleanupEndOfAssemblyAndNone.DisposeAsync was called\r\nLifeCycleClassCleanupEndOfAssemblyAndNone.Dispose was called" : "LifeCycleClassCleanupEndOfAssemblyAndNone.Dispose was called")} - """); + """, + caseClassCleanupEndOfAssemblyAndNone.Messages[2].Text); - Microsoft.VisualStudio.TestPlatform.ObjectModel.TestResult caseClassCleanupEndOfClassAndBeforeEachDerivedClass = RunEventsHandler.PassedTests.Single(x => x.TestCase.FullyQualifiedName.Contains("LifeCycleClassCleanupEndOfClassAndBeforeEachDerivedClass.TestMethod")); - caseClassCleanupEndOfClassAndBeforeEachDerivedClass.Outcome.Should().Be(Microsoft.VisualStudio.TestPlatform.ObjectModel.TestOutcome.Passed); - caseClassCleanupEndOfClassAndBeforeEachDerivedClass.Messages.Should().HaveCount(3); - caseClassCleanupEndOfClassAndBeforeEachDerivedClass.Messages[0].Text.Should().Be( + TestResult caseClassCleanupEndOfClassAndBeforeEachDerivedClass = RunEventsHandler.PassedTests.Single(x => x.TestCase.FullyQualifiedName.Contains("LifeCycleClassCleanupEndOfClassAndBeforeEachDerivedClass.TestMethod")); + Assert.AreEqual(TestOutcome.Passed, caseClassCleanupEndOfClassAndBeforeEachDerivedClass.Outcome); + Assert.HasCount(3, caseClassCleanupEndOfClassAndBeforeEachDerivedClass.Messages); + Assert.AreEqual( $""" Console: LifeCycleClassCleanupEndOfClassAndBeforeEachDerivedClass.ClassInitialize was called Console: LifeCycleClassCleanupEndOfClassAndBeforeEachDerivedClass.ctor was called @@ -1001,8 +928,9 @@ LifeCycleClassCleanupEndOfAssemblyAndNone.TestCleanup was called : "Console: LifeCycleClassCleanupEndOfClassAndBeforeEachDerivedClass.Dispose was called")} Console: LifeCycleClassCleanupEndOfClassAndBeforeEachDerivedClass.ClassCleanup was called - """); - caseClassCleanupEndOfClassAndBeforeEachDerivedClass.Messages[1].Text.Should().Be( + """, + caseClassCleanupEndOfClassAndBeforeEachDerivedClass.Messages[0].Text); + Assert.AreEqual( $""" @@ -1019,8 +947,9 @@ LifeCycleClassCleanupEndOfAssemblyAndNone.TestCleanup was called : GenerateTraceDebugPrefixedMessage("LifeCycleClassCleanupEndOfClassAndBeforeEachDerivedClass.Dispose was called"))} {GenerateTraceDebugPrefixedMessage("LifeCycleClassCleanupEndOfClassAndBeforeEachDerivedClass.ClassCleanup was called")} - """); - caseClassCleanupEndOfClassAndBeforeEachDerivedClass.Messages[2].Text.Should().Be( + """, + caseClassCleanupEndOfClassAndBeforeEachDerivedClass.Messages[1].Text); + Assert.AreEqual( $""" @@ -1035,12 +964,13 @@ LifeCycleClassCleanupEndOfClassAndBeforeEachDerivedClass.TestCleanup was called : "LifeCycleClassCleanupEndOfClassAndBeforeEachDerivedClass.Dispose was called")} LifeCycleClassCleanupEndOfClassAndBeforeEachDerivedClass.ClassCleanup was called - """); + """, + caseClassCleanupEndOfClassAndBeforeEachDerivedClass.Messages[2].Text); - Microsoft.VisualStudio.TestPlatform.ObjectModel.TestResult caseClassCleanupEndOfClassAndNone = RunEventsHandler.PassedTests.Single(x => x.TestCase.FullyQualifiedName.Contains("LifeCycleClassCleanupEndOfClassAndNone.TestMethod")); - caseClassCleanupEndOfClassAndNone.Outcome.Should().Be(Microsoft.VisualStudio.TestPlatform.ObjectModel.TestOutcome.Passed); - caseClassCleanupEndOfClassAndNone.Messages.Should().HaveCount(3); - caseClassCleanupEndOfClassAndNone.Messages[0].Text.Should().Be( + TestResult caseClassCleanupEndOfClassAndNone = RunEventsHandler.PassedTests.Single(x => x.TestCase.FullyQualifiedName.Contains("LifeCycleClassCleanupEndOfClassAndNone.TestMethod")); + Assert.AreEqual(TestOutcome.Passed, caseClassCleanupEndOfClassAndNone.Outcome); + Assert.HasCount(3, caseClassCleanupEndOfClassAndNone.Messages); + Assert.AreEqual( $""" Console: LifeCycleClassCleanupEndOfClassAndNone.ClassInitialize was called Console: LifeCycleClassCleanupEndOfClassAndNone.ctor was called @@ -1052,8 +982,9 @@ LifeCycleClassCleanupEndOfClassAndBeforeEachDerivedClass.ClassCleanup was called : "Console: LifeCycleClassCleanupEndOfClassAndNone.Dispose was called")} Console: LifeCycleClassCleanupEndOfClassAndNone.ClassCleanup was called - """); - caseClassCleanupEndOfClassAndNone.Messages[1].Text.Should().Be( + """, + caseClassCleanupEndOfClassAndNone.Messages[0].Text); + Assert.AreEqual( $""" @@ -1070,8 +1001,9 @@ LifeCycleClassCleanupEndOfClassAndBeforeEachDerivedClass.ClassCleanup was called : GenerateTraceDebugPrefixedMessage("LifeCycleClassCleanupEndOfClassAndNone.Dispose was called"))} {GenerateTraceDebugPrefixedMessage("LifeCycleClassCleanupEndOfClassAndNone.ClassCleanup was called")} - """); - caseClassCleanupEndOfClassAndNone.Messages[2].Text.Should().Be( + """, + caseClassCleanupEndOfClassAndNone.Messages[1].Text); + Assert.AreEqual( $""" @@ -1086,12 +1018,13 @@ LifeCycleClassCleanupEndOfClassAndNone.TestCleanup was called : "LifeCycleClassCleanupEndOfClassAndNone.Dispose was called")} LifeCycleClassCleanupEndOfClassAndNone.ClassCleanup was called - """); + """, + caseClassCleanupEndOfClassAndNone.Messages[2].Text); - Microsoft.VisualStudio.TestPlatform.ObjectModel.TestResult caseDerivedClassCleanupEndOfAssemblyAndBeforeEachDerivedClass = RunEventsHandler.PassedTests.Single(x => x.TestCase.FullyQualifiedName.Contains("LifeCycleDerivedClassCleanupEndOfAssemblyAndBeforeEachDerivedClass.DerivedClassTestMethod")); - caseDerivedClassCleanupEndOfAssemblyAndBeforeEachDerivedClass.Outcome.Should().Be(Microsoft.VisualStudio.TestPlatform.ObjectModel.TestOutcome.Passed); - caseDerivedClassCleanupEndOfAssemblyAndBeforeEachDerivedClass.Messages.Should().HaveCount(3); - caseDerivedClassCleanupEndOfAssemblyAndBeforeEachDerivedClass.Messages[0].Text.Should().Be( + TestResult caseDerivedClassCleanupEndOfAssemblyAndBeforeEachDerivedClass = RunEventsHandler.PassedTests.Single(x => x.TestCase.FullyQualifiedName.Contains("LifeCycleDerivedClassCleanupEndOfAssemblyAndBeforeEachDerivedClass.DerivedClassTestMethod")); + Assert.AreEqual(TestOutcome.Passed, caseDerivedClassCleanupEndOfAssemblyAndBeforeEachDerivedClass.Outcome); + Assert.HasCount(3, caseDerivedClassCleanupEndOfAssemblyAndBeforeEachDerivedClass.Messages); + Assert.AreEqual( $""" Console: LifeCycleDerivedClassCleanupEndOfAssemblyAndBeforeEachDerivedClass.ClassInitialize was called Console: LifeCycleClassCleanupEndOfAssemblyAndBeforeEachDerivedClass.ctor was called @@ -1105,8 +1038,9 @@ LifeCycleClassCleanupEndOfClassAndNone.ClassCleanup was called ? "Console: LifeCycleClassCleanupEndOfAssemblyAndBeforeEachDerivedClass.DisposeAsync was called\r\nConsole: LifeCycleClassCleanupEndOfAssemblyAndBeforeEachDerivedClass.Dispose was called" : "Console: LifeCycleClassCleanupEndOfAssemblyAndBeforeEachDerivedClass.Dispose was called")} - """); - caseDerivedClassCleanupEndOfAssemblyAndBeforeEachDerivedClass.Messages[1].Text.Should().Be( + """, + caseDerivedClassCleanupEndOfAssemblyAndBeforeEachDerivedClass.Messages[0].Text); + Assert.AreEqual( $""" @@ -1125,8 +1059,9 @@ LifeCycleClassCleanupEndOfClassAndNone.ClassCleanup was called + GenerateTraceDebugPrefixedMessage("LifeCycleClassCleanupEndOfAssemblyAndBeforeEachDerivedClass.Dispose was called") : GenerateTraceDebugPrefixedMessage("LifeCycleClassCleanupEndOfAssemblyAndBeforeEachDerivedClass.Dispose was called"))} - """); - caseDerivedClassCleanupEndOfAssemblyAndBeforeEachDerivedClass.Messages[2].Text.Should().Be( + """, + caseDerivedClassCleanupEndOfAssemblyAndBeforeEachDerivedClass.Messages[1].Text); + Assert.AreEqual( $""" @@ -1143,12 +1078,13 @@ LifeCycleClassCleanupEndOfAssemblyAndBeforeEachDerivedClass.TestCleanup was call ? "LifeCycleClassCleanupEndOfAssemblyAndBeforeEachDerivedClass.DisposeAsync was called\r\nLifeCycleClassCleanupEndOfAssemblyAndBeforeEachDerivedClass.Dispose was called" : "LifeCycleClassCleanupEndOfAssemblyAndBeforeEachDerivedClass.Dispose was called")} - """); + """, + caseDerivedClassCleanupEndOfAssemblyAndBeforeEachDerivedClass.Messages[2].Text); - Microsoft.VisualStudio.TestPlatform.ObjectModel.TestResult caseDerivedClassCleanupEndOfAssemblyAndNone = RunEventsHandler.PassedTests.Single(x => x.TestCase.FullyQualifiedName.Contains("LifeCycleDerivedClassCleanupEndOfAssemblyAndNone.DerivedClassTestMethod")); - caseDerivedClassCleanupEndOfAssemblyAndNone.Outcome.Should().Be(Microsoft.VisualStudio.TestPlatform.ObjectModel.TestOutcome.Passed); - caseDerivedClassCleanupEndOfAssemblyAndNone.Messages.Should().HaveCount(3); - caseDerivedClassCleanupEndOfAssemblyAndNone.Messages[0].Text.Should().Be( + TestResult caseDerivedClassCleanupEndOfAssemblyAndNone = RunEventsHandler.PassedTests.Single(x => x.TestCase.FullyQualifiedName.Contains("LifeCycleDerivedClassCleanupEndOfAssemblyAndNone.DerivedClassTestMethod")); + Assert.AreEqual(TestOutcome.Passed, caseDerivedClassCleanupEndOfAssemblyAndNone.Outcome); + Assert.HasCount(3, caseDerivedClassCleanupEndOfAssemblyAndNone.Messages); + Assert.AreEqual( $""" Console: LifeCycleDerivedClassCleanupEndOfAssemblyAndNone.ClassInitialize was called Console: LifeCycleClassCleanupEndOfAssemblyAndNone.ctor was called @@ -1162,8 +1098,9 @@ LifeCycleClassCleanupEndOfAssemblyAndBeforeEachDerivedClass.TestCleanup was call ? "Console: LifeCycleClassCleanupEndOfAssemblyAndNone.DisposeAsync was called\r\nConsole: LifeCycleClassCleanupEndOfAssemblyAndNone.Dispose was called" : "Console: LifeCycleClassCleanupEndOfAssemblyAndNone.Dispose was called")} - """); - caseDerivedClassCleanupEndOfAssemblyAndNone.Messages[1].Text.Should().Be( + """, + caseDerivedClassCleanupEndOfAssemblyAndNone.Messages[0].Text); + Assert.AreEqual( $""" @@ -1182,8 +1119,9 @@ LifeCycleClassCleanupEndOfAssemblyAndBeforeEachDerivedClass.TestCleanup was call + GenerateTraceDebugPrefixedMessage("LifeCycleClassCleanupEndOfAssemblyAndNone.Dispose was called") : GenerateTraceDebugPrefixedMessage("LifeCycleClassCleanupEndOfAssemblyAndNone.Dispose was called"))} - """); - caseDerivedClassCleanupEndOfAssemblyAndNone.Messages[2].Text.Should().Be( + """, + caseDerivedClassCleanupEndOfAssemblyAndNone.Messages[1].Text); + Assert.AreEqual( $""" @@ -1200,11 +1138,12 @@ LifeCycleClassCleanupEndOfAssemblyAndNone.TestCleanup was called ? "LifeCycleClassCleanupEndOfAssemblyAndNone.DisposeAsync was called\r\nLifeCycleClassCleanupEndOfAssemblyAndNone.Dispose was called" : "LifeCycleClassCleanupEndOfAssemblyAndNone.Dispose was called")} - """); - Microsoft.VisualStudio.TestPlatform.ObjectModel.TestResult caseDerivedClassCleanupEndOfClassAndBeforeEachDerivedClass = RunEventsHandler.PassedTests.Single(x => x.TestCase.FullyQualifiedName.Contains("LifeCycleDerivedClassCleanupEndOfClassAndBeforeEachDerivedClass.DerivedClassTestMethod")); - caseDerivedClassCleanupEndOfClassAndBeforeEachDerivedClass.Outcome.Should().Be(Microsoft.VisualStudio.TestPlatform.ObjectModel.TestOutcome.Passed); - caseDerivedClassCleanupEndOfClassAndBeforeEachDerivedClass.Messages.Should().HaveCount(3); - caseDerivedClassCleanupEndOfClassAndBeforeEachDerivedClass.Messages[0].Text.Should().Be( + """, + caseDerivedClassCleanupEndOfAssemblyAndNone.Messages[2].Text); + TestResult caseDerivedClassCleanupEndOfClassAndBeforeEachDerivedClass = RunEventsHandler.PassedTests.Single(x => x.TestCase.FullyQualifiedName.Contains("LifeCycleDerivedClassCleanupEndOfClassAndBeforeEachDerivedClass.DerivedClassTestMethod")); + Assert.AreEqual(TestOutcome.Passed, caseDerivedClassCleanupEndOfClassAndBeforeEachDerivedClass.Outcome); + Assert.HasCount(3, caseDerivedClassCleanupEndOfClassAndBeforeEachDerivedClass.Messages); + Assert.AreEqual( $""" Console: LifeCycleDerivedClassCleanupEndOfClassAndBeforeEachDerivedClass.ClassInitialize was called Console: LifeCycleClassCleanupEndOfClassAndBeforeEachDerivedClass.ctor was called @@ -1218,8 +1157,9 @@ LifeCycleClassCleanupEndOfAssemblyAndNone.TestCleanup was called ? "Console: LifeCycleClassCleanupEndOfClassAndBeforeEachDerivedClass.DisposeAsync was called\r\nConsole: LifeCycleClassCleanupEndOfClassAndBeforeEachDerivedClass.Dispose was called" : "Console: LifeCycleClassCleanupEndOfClassAndBeforeEachDerivedClass.Dispose was called")} - """); - caseDerivedClassCleanupEndOfClassAndBeforeEachDerivedClass.Messages[1].Text.Should().Be( + """, + caseDerivedClassCleanupEndOfClassAndBeforeEachDerivedClass.Messages[0].Text); + Assert.AreEqual( $""" @@ -1238,8 +1178,9 @@ LifeCycleClassCleanupEndOfAssemblyAndNone.TestCleanup was called + GenerateTraceDebugPrefixedMessage("LifeCycleClassCleanupEndOfClassAndBeforeEachDerivedClass.Dispose was called") : GenerateTraceDebugPrefixedMessage("LifeCycleClassCleanupEndOfClassAndBeforeEachDerivedClass.Dispose was called"))} - """); - caseDerivedClassCleanupEndOfClassAndBeforeEachDerivedClass.Messages[2].Text.Should().Be( + """, + caseDerivedClassCleanupEndOfClassAndBeforeEachDerivedClass.Messages[1].Text); + Assert.AreEqual( $""" @@ -1256,12 +1197,13 @@ LifeCycleClassCleanupEndOfClassAndBeforeEachDerivedClass.TestCleanup was called ? "LifeCycleClassCleanupEndOfClassAndBeforeEachDerivedClass.DisposeAsync was called\r\nLifeCycleClassCleanupEndOfClassAndBeforeEachDerivedClass.Dispose was called" : "LifeCycleClassCleanupEndOfClassAndBeforeEachDerivedClass.Dispose was called")} - """); + """, + caseDerivedClassCleanupEndOfClassAndBeforeEachDerivedClass.Messages[2].Text); - Microsoft.VisualStudio.TestPlatform.ObjectModel.TestResult caseDerivedClassCleanupEndOfClassAndNone = RunEventsHandler.PassedTests.Single(x => x.TestCase.FullyQualifiedName.Contains("LifeCycleDerivedClassCleanupEndOfClassAndNone.DerivedClassTestMethod")); - caseDerivedClassCleanupEndOfClassAndNone.Outcome.Should().Be(Microsoft.VisualStudio.TestPlatform.ObjectModel.TestOutcome.Passed); - caseDerivedClassCleanupEndOfClassAndNone.Messages.Should().HaveCount(3); - caseDerivedClassCleanupEndOfClassAndNone.Messages[0].Text.Should().Be( + TestResult caseDerivedClassCleanupEndOfClassAndNone = RunEventsHandler.PassedTests.Single(x => x.TestCase.FullyQualifiedName.Contains("LifeCycleDerivedClassCleanupEndOfClassAndNone.DerivedClassTestMethod")); + Assert.AreEqual(TestOutcome.Passed, caseDerivedClassCleanupEndOfClassAndNone.Outcome); + Assert.HasCount(3, caseDerivedClassCleanupEndOfClassAndNone.Messages); + Assert.AreEqual( $""" Console: LifeCycleDerivedClassCleanupEndOfClassAndNone.ClassInitialize was called Console: LifeCycleClassCleanupEndOfClassAndNone.ctor was called @@ -1275,8 +1217,9 @@ LifeCycleClassCleanupEndOfClassAndBeforeEachDerivedClass.TestCleanup was called ? "Console: LifeCycleClassCleanupEndOfClassAndNone.DisposeAsync was called\r\nConsole: LifeCycleClassCleanupEndOfClassAndNone.Dispose was called" : "Console: LifeCycleClassCleanupEndOfClassAndNone.Dispose was called")} - """); - caseDerivedClassCleanupEndOfClassAndNone.Messages[1].Text.Should().Be( + """, + caseDerivedClassCleanupEndOfClassAndNone.Messages[0].Text); + Assert.AreEqual( $""" @@ -1295,8 +1238,9 @@ LifeCycleClassCleanupEndOfClassAndBeforeEachDerivedClass.TestCleanup was called + GenerateTraceDebugPrefixedMessage("LifeCycleClassCleanupEndOfClassAndNone.Dispose was called") : GenerateTraceDebugPrefixedMessage("LifeCycleClassCleanupEndOfClassAndNone.Dispose was called"))} - """); - caseDerivedClassCleanupEndOfClassAndNone.Messages[2].Text.Should().Be( + """, + caseDerivedClassCleanupEndOfClassAndNone.Messages[1].Text); + Assert.AreEqual( $""" @@ -1313,12 +1257,13 @@ LifeCycleClassCleanupEndOfClassAndNone.TestCleanup was called ? "LifeCycleClassCleanupEndOfClassAndNone.DisposeAsync was called\r\nLifeCycleClassCleanupEndOfClassAndNone.Dispose was called" : "LifeCycleClassCleanupEndOfClassAndNone.Dispose was called")} - """); + """, + caseDerivedClassCleanupEndOfClassAndNone.Messages[2].Text); - Microsoft.VisualStudio.TestPlatform.ObjectModel.TestResult caseDerivedClassCleanupEndOfAssemblyAndBeforeEachDerivedClassParentTestMethod = RunEventsHandler.PassedTests.Single(x => x.TestCase.FullyQualifiedName.Contains("LifeCycleDerivedClassCleanupEndOfAssemblyAndBeforeEachDerivedClass.TestMethod")); - caseDerivedClassCleanupEndOfAssemblyAndBeforeEachDerivedClassParentTestMethod.Outcome.Should().Be(Microsoft.VisualStudio.TestPlatform.ObjectModel.TestOutcome.Passed); - caseDerivedClassCleanupEndOfAssemblyAndBeforeEachDerivedClassParentTestMethod.Messages.Should().HaveCount(3); - caseDerivedClassCleanupEndOfAssemblyAndBeforeEachDerivedClassParentTestMethod.Messages[0].Text.Should().Be( + TestResult caseDerivedClassCleanupEndOfAssemblyAndBeforeEachDerivedClassParentTestMethod = RunEventsHandler.PassedTests.Single(x => x.TestCase.FullyQualifiedName.Contains("LifeCycleDerivedClassCleanupEndOfAssemblyAndBeforeEachDerivedClass.TestMethod")); + Assert.AreEqual(TestOutcome.Passed, caseDerivedClassCleanupEndOfAssemblyAndBeforeEachDerivedClassParentTestMethod.Outcome); + Assert.HasCount(3, caseDerivedClassCleanupEndOfAssemblyAndBeforeEachDerivedClassParentTestMethod.Messages); + Assert.AreEqual( $""" Console: LifeCycleClassCleanupEndOfAssemblyAndBeforeEachDerivedClass.ctor was called Console: LifeCycleDerivedClassCleanupEndOfAssemblyAndBeforeEachDerivedClass.ctor was called @@ -1331,8 +1276,9 @@ LifeCycleClassCleanupEndOfClassAndNone.TestCleanup was called ? "Console: LifeCycleClassCleanupEndOfAssemblyAndBeforeEachDerivedClass.DisposeAsync was called\r\nConsole: LifeCycleClassCleanupEndOfAssemblyAndBeforeEachDerivedClass.Dispose was called" : "Console: LifeCycleClassCleanupEndOfAssemblyAndBeforeEachDerivedClass.Dispose was called")} - """); - caseDerivedClassCleanupEndOfAssemblyAndBeforeEachDerivedClassParentTestMethod.Messages[1].Text.Should().Be( + """, + caseDerivedClassCleanupEndOfAssemblyAndBeforeEachDerivedClassParentTestMethod.Messages[0].Text); + Assert.AreEqual( $""" @@ -1350,8 +1296,9 @@ LifeCycleClassCleanupEndOfClassAndNone.TestCleanup was called + GenerateTraceDebugPrefixedMessage("LifeCycleClassCleanupEndOfAssemblyAndBeforeEachDerivedClass.Dispose was called") : GenerateTraceDebugPrefixedMessage("LifeCycleClassCleanupEndOfAssemblyAndBeforeEachDerivedClass.Dispose was called"))} - """); - caseDerivedClassCleanupEndOfAssemblyAndBeforeEachDerivedClassParentTestMethod.Messages[2].Text.Should().Be( + """, + caseDerivedClassCleanupEndOfAssemblyAndBeforeEachDerivedClassParentTestMethod.Messages[1].Text); + Assert.AreEqual( $""" @@ -1367,12 +1314,13 @@ LifeCycleClassCleanupEndOfAssemblyAndBeforeEachDerivedClass.TestCleanup was call ? "LifeCycleClassCleanupEndOfAssemblyAndBeforeEachDerivedClass.DisposeAsync was called\r\nLifeCycleClassCleanupEndOfAssemblyAndBeforeEachDerivedClass.Dispose was called" : "LifeCycleClassCleanupEndOfAssemblyAndBeforeEachDerivedClass.Dispose was called")} - """); + """, + caseDerivedClassCleanupEndOfAssemblyAndBeforeEachDerivedClassParentTestMethod.Messages[2].Text); - Microsoft.VisualStudio.TestPlatform.ObjectModel.TestResult caseDerivedClassCleanupEndOfAssemblyAndNoneParentTestMethod = RunEventsHandler.PassedTests.Single(x => x.TestCase.FullyQualifiedName.Contains("LifeCycleDerivedClassCleanupEndOfAssemblyAndNone.TestMethod")); - caseDerivedClassCleanupEndOfAssemblyAndNoneParentTestMethod.Outcome.Should().Be(Microsoft.VisualStudio.TestPlatform.ObjectModel.TestOutcome.Passed); - caseDerivedClassCleanupEndOfAssemblyAndNoneParentTestMethod.Messages.Should().HaveCount(3); - caseDerivedClassCleanupEndOfAssemblyAndNoneParentTestMethod.Messages[0].Text.Should().Be( + TestResult caseDerivedClassCleanupEndOfAssemblyAndNoneParentTestMethod = RunEventsHandler.PassedTests.Single(x => x.TestCase.FullyQualifiedName.Contains("LifeCycleDerivedClassCleanupEndOfAssemblyAndNone.TestMethod")); + Assert.AreEqual(TestOutcome.Passed, caseDerivedClassCleanupEndOfAssemblyAndNoneParentTestMethod.Outcome); + Assert.HasCount(3, caseDerivedClassCleanupEndOfAssemblyAndNoneParentTestMethod.Messages); + Assert.AreEqual( $""" Console: LifeCycleClassCleanupEndOfAssemblyAndNone.ctor was called Console: LifeCycleDerivedClassCleanupEndOfAssemblyAndNone.ctor was called @@ -1385,8 +1333,9 @@ LifeCycleClassCleanupEndOfAssemblyAndBeforeEachDerivedClass.TestCleanup was call ? "Console: LifeCycleClassCleanupEndOfAssemblyAndNone.DisposeAsync was called\r\nConsole: LifeCycleClassCleanupEndOfAssemblyAndNone.Dispose was called" : "Console: LifeCycleClassCleanupEndOfAssemblyAndNone.Dispose was called")} - """); - caseDerivedClassCleanupEndOfAssemblyAndNoneParentTestMethod.Messages[1].Text.Should().Be( + """, + caseDerivedClassCleanupEndOfAssemblyAndNoneParentTestMethod.Messages[0].Text); + Assert.AreEqual( $""" @@ -1404,8 +1353,9 @@ LifeCycleClassCleanupEndOfAssemblyAndBeforeEachDerivedClass.TestCleanup was call + GenerateTraceDebugPrefixedMessage("LifeCycleClassCleanupEndOfAssemblyAndNone.Dispose was called") : GenerateTraceDebugPrefixedMessage("LifeCycleClassCleanupEndOfAssemblyAndNone.Dispose was called"))} - """); - caseDerivedClassCleanupEndOfAssemblyAndNoneParentTestMethod.Messages[2].Text.Should().Be( + """, + caseDerivedClassCleanupEndOfAssemblyAndNoneParentTestMethod.Messages[1].Text); + Assert.AreEqual( $""" @@ -1421,11 +1371,12 @@ LifeCycleClassCleanupEndOfAssemblyAndNone.TestCleanup was called ? "LifeCycleClassCleanupEndOfAssemblyAndNone.DisposeAsync was called\r\nLifeCycleClassCleanupEndOfAssemblyAndNone.Dispose was called" : "LifeCycleClassCleanupEndOfAssemblyAndNone.Dispose was called")} - """); - Microsoft.VisualStudio.TestPlatform.ObjectModel.TestResult caseDerivedClassCleanupEndOfClassAndBeforeEachDerivedClassParentTestMethod = RunEventsHandler.PassedTests.Single(x => x.TestCase.FullyQualifiedName.Contains("LifeCycleDerivedClassCleanupEndOfClassAndBeforeEachDerivedClass.TestMethod")); - caseDerivedClassCleanupEndOfClassAndBeforeEachDerivedClassParentTestMethod.Outcome.Should().Be(Microsoft.VisualStudio.TestPlatform.ObjectModel.TestOutcome.Passed); - caseDerivedClassCleanupEndOfClassAndBeforeEachDerivedClassParentTestMethod.Messages.Should().HaveCount(3); - caseDerivedClassCleanupEndOfClassAndBeforeEachDerivedClassParentTestMethod.Messages[0].Text.Should().Be( + """, + caseDerivedClassCleanupEndOfAssemblyAndNoneParentTestMethod.Messages[2].Text); + TestResult caseDerivedClassCleanupEndOfClassAndBeforeEachDerivedClassParentTestMethod = RunEventsHandler.PassedTests.Single(x => x.TestCase.FullyQualifiedName.Contains("LifeCycleDerivedClassCleanupEndOfClassAndBeforeEachDerivedClass.TestMethod")); + Assert.AreEqual(TestOutcome.Passed, caseDerivedClassCleanupEndOfClassAndBeforeEachDerivedClassParentTestMethod.Outcome); + Assert.HasCount(3, caseDerivedClassCleanupEndOfClassAndBeforeEachDerivedClassParentTestMethod.Messages); + Assert.AreEqual( $""" Console: LifeCycleClassCleanupEndOfClassAndBeforeEachDerivedClass.ctor was called Console: LifeCycleDerivedClassCleanupEndOfClassAndBeforeEachDerivedClass.ctor was called @@ -1440,8 +1391,9 @@ LifeCycleClassCleanupEndOfAssemblyAndNone.TestCleanup was called Console: LifeCycleDerivedClassCleanupEndOfClassAndBeforeEachDerivedClass.ClassCleanup was called Console: LifeCycleClassCleanupEndOfClassAndBeforeEachDerivedClass.ClassCleanup was called - """); - caseDerivedClassCleanupEndOfClassAndBeforeEachDerivedClassParentTestMethod.Messages[1].Text.Should().Be( + """, + caseDerivedClassCleanupEndOfClassAndBeforeEachDerivedClassParentTestMethod.Messages[0].Text); + Assert.AreEqual( $""" @@ -1461,8 +1413,9 @@ LifeCycleClassCleanupEndOfAssemblyAndNone.TestCleanup was called {GenerateTraceDebugPrefixedMessage("LifeCycleDerivedClassCleanupEndOfClassAndBeforeEachDerivedClass.ClassCleanup was called")} {GenerateTraceDebugPrefixedMessage("LifeCycleClassCleanupEndOfClassAndBeforeEachDerivedClass.ClassCleanup was called")} - """); - caseDerivedClassCleanupEndOfClassAndBeforeEachDerivedClassParentTestMethod.Messages[2].Text.Should().Be( + """, + caseDerivedClassCleanupEndOfClassAndBeforeEachDerivedClassParentTestMethod.Messages[1].Text); + Assert.AreEqual( $""" @@ -1480,12 +1433,13 @@ LifeCycleClassCleanupEndOfClassAndBeforeEachDerivedClass.TestCleanup was called LifeCycleDerivedClassCleanupEndOfClassAndBeforeEachDerivedClass.ClassCleanup was called LifeCycleClassCleanupEndOfClassAndBeforeEachDerivedClass.ClassCleanup was called - """); + """, + caseDerivedClassCleanupEndOfClassAndBeforeEachDerivedClassParentTestMethod.Messages[2].Text); - Microsoft.VisualStudio.TestPlatform.ObjectModel.TestResult caseDerivedClassCleanupEndOfClassAndNoneParentTestMethod = RunEventsHandler.PassedTests.Single(x => x.TestCase.FullyQualifiedName.Contains("LifeCycleDerivedClassCleanupEndOfClassAndNone.TestMethod")); - caseDerivedClassCleanupEndOfClassAndNoneParentTestMethod.Outcome.Should().Be(Microsoft.VisualStudio.TestPlatform.ObjectModel.TestOutcome.Passed); - caseDerivedClassCleanupEndOfClassAndNoneParentTestMethod.Messages.Should().HaveCount(3); - caseDerivedClassCleanupEndOfClassAndNoneParentTestMethod.Messages[0].Text.Should().Be( + TestResult caseDerivedClassCleanupEndOfClassAndNoneParentTestMethod = RunEventsHandler.PassedTests.Single(x => x.TestCase.FullyQualifiedName.Contains("LifeCycleDerivedClassCleanupEndOfClassAndNone.TestMethod")); + Assert.AreEqual(TestOutcome.Passed, caseDerivedClassCleanupEndOfClassAndNoneParentTestMethod.Outcome); + Assert.HasCount(3, caseDerivedClassCleanupEndOfClassAndNoneParentTestMethod.Messages); + Assert.AreEqual( $""" Console: LifeCycleClassCleanupEndOfClassAndNone.ctor was called Console: LifeCycleDerivedClassCleanupEndOfClassAndNone.ctor was called @@ -1498,8 +1452,9 @@ LifeCycleClassCleanupEndOfClassAndBeforeEachDerivedClass.ClassCleanup was called ? "Console: LifeCycleClassCleanupEndOfClassAndNone.DisposeAsync was called\r\nConsole: LifeCycleClassCleanupEndOfClassAndNone.Dispose was called" : "Console: LifeCycleClassCleanupEndOfClassAndNone.Dispose was called")} - """); - caseDerivedClassCleanupEndOfClassAndNoneParentTestMethod.Messages[1].Text.Should().Be( + """, + caseDerivedClassCleanupEndOfClassAndNoneParentTestMethod.Messages[0].Text); + Assert.AreEqual( $""" @@ -1517,8 +1472,9 @@ LifeCycleClassCleanupEndOfClassAndBeforeEachDerivedClass.ClassCleanup was called + GenerateTraceDebugPrefixedMessage("LifeCycleClassCleanupEndOfClassAndNone.Dispose was called") : GenerateTraceDebugPrefixedMessage("LifeCycleClassCleanupEndOfClassAndNone.Dispose was called"))} - """); - caseDerivedClassCleanupEndOfClassAndNoneParentTestMethod.Messages[2].Text.Should().Be( + """, + caseDerivedClassCleanupEndOfClassAndNoneParentTestMethod.Messages[1].Text); + Assert.AreEqual( $""" @@ -1534,14 +1490,15 @@ LifeCycleClassCleanupEndOfClassAndNone.TestCleanup was called ? "LifeCycleClassCleanupEndOfClassAndNone.DisposeAsync was called\r\nLifeCycleClassCleanupEndOfClassAndNone.Dispose was called" : "LifeCycleClassCleanupEndOfClassAndNone.Dispose was called")} - """); + """, + caseDerivedClassCleanupEndOfClassAndNoneParentTestMethod.Messages[2].Text); // Test the parent test method. // We are seeing all the ClassCleanup EndOfAssembly (or nothing set - as it's the default) being reported // here as this is the last test to run. - Microsoft.VisualStudio.TestPlatform.ObjectModel.TestResult caseDerivedClassInitializeNoneAndClassCleanupBeforeEachDerivedClassParentTestMethod = RunEventsHandler.PassedTests.Single(x => x.TestCase.FullyQualifiedName.Contains("LifeCycleDerivedClassInitializeNoneAndClassCleanupBeforeEachDerivedClass.TestMethod")); - caseDerivedClassInitializeNoneAndClassCleanupBeforeEachDerivedClassParentTestMethod.Outcome.Should().Be(Microsoft.VisualStudio.TestPlatform.ObjectModel.TestOutcome.Passed); - caseDerivedClassInitializeNoneAndClassCleanupBeforeEachDerivedClassParentTestMethod.Messages.Should().HaveCount(3); + TestResult caseDerivedClassInitializeNoneAndClassCleanupBeforeEachDerivedClassParentTestMethod = RunEventsHandler.PassedTests.Single(x => x.TestCase.FullyQualifiedName.Contains("LifeCycleDerivedClassInitializeNoneAndClassCleanupBeforeEachDerivedClass.TestMethod")); + Assert.AreEqual(TestOutcome.Passed, caseDerivedClassInitializeNoneAndClassCleanupBeforeEachDerivedClassParentTestMethod.Outcome); + Assert.HasCount(3, caseDerivedClassInitializeNoneAndClassCleanupBeforeEachDerivedClassParentTestMethod.Messages); // Locally, netfx calls seems to be respecting the order of the cleanup while it is not stable for netcore. // But local order is not the same on various machines. I am not sure whether we should be committing to a @@ -1560,9 +1517,7 @@ LifeCycleClassCleanupEndOfClassAndNone.TestCleanup was called : "Console: LifeCycleClassInitializeNoneAndClassCleanupBeforeEachDerivedClass.Dispose was called")} """; - caseDerivedClassInitializeNoneAndClassCleanupBeforeEachDerivedClassParentTestMethod - .Messages[0].Text - .Should().StartWith(expectedStart); + Assert.StartsWith(expectedStart, caseDerivedClassInitializeNoneAndClassCleanupBeforeEachDerivedClassParentTestMethod.Messages[0].Text); string[] expectedRemainingMessages = """ @@ -1588,11 +1543,12 @@ LifeCycleClassCleanupEndOfClassAndNone.TestCleanup was called """ .Split(WindowsLineReturn, StringSplitOptions.None); - caseDerivedClassInitializeNoneAndClassCleanupBeforeEachDerivedClassParentTestMethod - .Messages[0].Text! - .Substring(expectedStart.Length) - .Split(WindowsLineReturn, StringSplitOptions.None) - .Should().BeEquivalentTo(expectedRemainingMessages); + CollectionAssert.AreEquivalent( + expectedRemainingMessages, + caseDerivedClassInitializeNoneAndClassCleanupBeforeEachDerivedClassParentTestMethod + .Messages[0].Text! + .Substring(expectedStart.Length) + .Split(WindowsLineReturn, StringSplitOptions.None)); expectedStart = $""" @@ -1613,9 +1569,7 @@ LifeCycleClassCleanupEndOfClassAndNone.TestCleanup was called : GenerateTraceDebugPrefixedMessage("LifeCycleClassInitializeNoneAndClassCleanupBeforeEachDerivedClass.Dispose was called"))} """; - caseDerivedClassInitializeNoneAndClassCleanupBeforeEachDerivedClassParentTestMethod - .Messages[1].Text - .Should().StartWith(expectedStart); + Assert.StartsWith(expectedStart, caseDerivedClassInitializeNoneAndClassCleanupBeforeEachDerivedClassParentTestMethod.Messages[1].Text); expectedRemainingMessages = $""" @@ -1641,11 +1595,12 @@ LifeCycleClassCleanupEndOfClassAndNone.TestCleanup was called """ .Split(["\r\n"], StringSplitOptions.None); - caseDerivedClassInitializeNoneAndClassCleanupBeforeEachDerivedClassParentTestMethod - .Messages[1].Text! - .Substring(expectedStart.Length) - .Split(["\r\n"], StringSplitOptions.None) - .Should().BeEquivalentTo(expectedRemainingMessages); + CollectionAssert.AreEquivalent( + expectedRemainingMessages, + caseDerivedClassInitializeNoneAndClassCleanupBeforeEachDerivedClassParentTestMethod + .Messages[1].Text! + .Substring(expectedStart.Length) + .Split(["\r\n"], StringSplitOptions.None)); expectedStart = $""" @@ -1664,9 +1619,7 @@ LifeCycleClassInitializeNoneAndClassCleanupBeforeEachDerivedClass.TestCleanup wa : "LifeCycleClassInitializeNoneAndClassCleanupBeforeEachDerivedClass.Dispose was called")} """; - caseDerivedClassInitializeNoneAndClassCleanupBeforeEachDerivedClassParentTestMethod - .Messages[2].Text - .Should().StartWith(expectedStart); + Assert.StartsWith(expectedStart, caseDerivedClassInitializeNoneAndClassCleanupBeforeEachDerivedClassParentTestMethod.Messages[2].Text); expectedRemainingMessages = """ @@ -1692,11 +1645,145 @@ AssemblyCleanup was called """ .Split(["\r\n"], StringSplitOptions.None); - caseDerivedClassInitializeNoneAndClassCleanupBeforeEachDerivedClassParentTestMethod - .Messages[2].Text! - .Substring(expectedStart.Length) - .Split(["\r\n"], StringSplitOptions.None) - .Should().BeEquivalentTo(expectedRemainingMessages); + CollectionAssert.AreEquivalent( + expectedRemainingMessages, + caseDerivedClassInitializeNoneAndClassCleanupBeforeEachDerivedClassParentTestMethod + .Messages[2].Text! + .Substring(expectedStart.Length) + .Split(["\r\n"], StringSplitOptions.None)); + } + + [TestMethod] + public void ValidateInheritanceBehavior() + { + InvokeVsTestForExecution( + [TestAssetName], + testCaseFilter: "FullyQualifiedName~LifecycleInheritance", + targetFramework: "net462"); + + Assert.HasCount(10, RunEventsHandler.PassedTests); + + TestResult testMethod1 = RunEventsHandler.PassedTests.Single(x => x.TestCase.FullyQualifiedName.EndsWith("TestClassDerived_EndOfClass.TestMethod", StringComparison.Ordinal)); + Assert.AreEqual( + """ + Console: AssemblyInit was called + TestClassBaseEndOfClass: ClassInitialize + TestClassDerived_EndOfClass: TestMethod + TestClassBaseEndOfClass: ClassCleanup + + """, + testMethod1.Messages[0].Text); + + TestResult testMethod2 = RunEventsHandler.PassedTests.Single(x => x.TestCase.FullyQualifiedName.EndsWith("TestClassDerived_EndOfAssembly.TestMethod", StringComparison.Ordinal)); + Assert.AreEqual( + """ + TestClassBaseEndOfAssembly: ClassInitialize + TestClassDerived_EndOfAssembly: TestMethod + + """, + testMethod2.Messages[0].Text); + + TestResult testMethod3 = RunEventsHandler.PassedTests.Single(x => x.TestCase.FullyQualifiedName.EndsWith("TestClassDerivedEndOfClass_EndOfClassEndOfClass.TestMethod", StringComparison.Ordinal)); + Assert.AreEqual( + """ + TestClassBaseEndOfClass: ClassInitialize + TestClassIntermediateEndOfClassBaseEndOfClass: ClassInitialize + TestClassDerivedEndOfClass_EndOfClassEndOfClass: TestMethod + TestClassDerivedEndOfClass_EndOfClassEndOfClass: ClassCleanup + TestClassIntermediateEndOfClassBaseEndOfClass: ClassCleanup + TestClassBaseEndOfClass: ClassCleanup + + """, + testMethod3.Messages[0].Text); + + TestResult testMethod4 = RunEventsHandler.PassedTests.Single(x => x.TestCase.FullyQualifiedName.EndsWith("TestClassDerived_EndOfClassEndOfClass.TestMethod", StringComparison.Ordinal)); + Assert.AreEqual( + """ + TestClassBaseEndOfClass: ClassInitialize + TestClassIntermediateEndOfClassBaseEndOfClass: ClassInitialize + TestClassDerived_EndOfClassEndOfClass: TestMethod + TestClassIntermediateEndOfClassBaseEndOfClass: ClassCleanup + TestClassBaseEndOfClass: ClassCleanup + + """, + testMethod4.Messages[0].Text); + + TestResult testMethod5 = RunEventsHandler.PassedTests.Single(x => x.TestCase.FullyQualifiedName.EndsWith("TestClassDerivedEndOfClass_EndOfClassEndOfAssembly.TestMethod", StringComparison.Ordinal)); + Assert.AreEqual( + """ + TestClassBaseEndOfAssembly: ClassInitialize + TestClassIntermediateEndOfClassBaseEndOfAssembly: ClassInitialize + TestClassDerivedEndOfClass_EndOfClassEndOfAssembly: TestMethod + TestClassDerivedEndOfClass_EndOfClassEndOfAssembly: ClassCleanup + TestClassIntermediateEndOfClassBaseEndOfAssembly: ClassCleanup + TestClassBaseEndOfAssembly: ClassCleanup + + """, + testMethod5.Messages[0].Text); + + TestResult testMethod6 = RunEventsHandler.PassedTests.Single(x => x.TestCase.FullyQualifiedName.EndsWith("TestClassDerived_EndOfClassEndOfAssembly.TestMethod", StringComparison.Ordinal)); + Assert.AreEqual( + """ + TestClassBaseEndOfAssembly: ClassInitialize + TestClassIntermediateEndOfClassBaseEndOfAssembly: ClassInitialize + TestClassDerived_EndOfClassEndOfAssembly: TestMethod + TestClassIntermediateEndOfClassBaseEndOfAssembly: ClassCleanup + TestClassBaseEndOfAssembly: ClassCleanup + + """, + testMethod6.Messages[0].Text); + + TestResult testMethod7 = RunEventsHandler.PassedTests.Single(x => x.TestCase.FullyQualifiedName.EndsWith("TestClassDerivedEndOfClass_EndOfAssemblyEndOfClass.TestMethod", StringComparison.Ordinal)); + Assert.AreEqual( + """ + TestClassBaseEndOfClass: ClassInitialize + TestClassIntermediateEndOfAssemblyBaseEndOfClass: ClassInitialize + TestClassDerivedEndOfClass_EndOfAssemblyEndOfClass: TestMethod + TestClassDerivedEndOfClass_EndOfAssemblyEndOfClass: ClassCleanup + TestClassIntermediateEndOfAssemblyBaseEndOfClass: ClassCleanup + TestClassBaseEndOfClass: ClassCleanup + + """, + testMethod7.Messages[0].Text); + + TestResult testMethod8 = RunEventsHandler.PassedTests.Single(x => x.TestCase.FullyQualifiedName.EndsWith("TestClassDerived_EndOfAssemblyEndOfClass.TestMethod", StringComparison.Ordinal)); + Assert.AreEqual( + """ + TestClassBaseEndOfClass: ClassInitialize + TestClassIntermediateEndOfAssemblyBaseEndOfClass: ClassInitialize + TestClassDerived_EndOfAssemblyEndOfClass: TestMethod + TestClassIntermediateEndOfAssemblyBaseEndOfClass: ClassCleanup + TestClassBaseEndOfClass: ClassCleanup + + """, + testMethod8.Messages[0].Text); + + TestResult testMethod9 = RunEventsHandler.PassedTests.Single(x => x.TestCase.FullyQualifiedName.EndsWith("TestClassDerivedEndOfClass_EndOfAssemblyEndOfAssembly.TestMethod", StringComparison.Ordinal)); + Assert.AreEqual( + """ + TestClassBaseEndOfAssembly: ClassInitialize + TestClassIntermediateEndOfAssemblyBaseEndOfAssembly: ClassInitialize + TestClassDerivedEndOfClass_EndOfAssemblyEndOfAssembly: TestMethod + TestClassDerivedEndOfClass_EndOfAssemblyEndOfAssembly: ClassCleanup + TestClassIntermediateEndOfAssemblyBaseEndOfAssembly: ClassCleanup + TestClassBaseEndOfAssembly: ClassCleanup + + """, + testMethod9.Messages[0].Text); + + TestResult testMethod10 = RunEventsHandler.PassedTests.Single(x => x.TestCase.FullyQualifiedName.EndsWith("TestClassDerived_EndOfAssemblyEndOfAssembly.TestMethod", StringComparison.Ordinal)); + Assert.AreEqual( + """ + TestClassBaseEndOfAssembly: ClassInitialize + TestClassIntermediateEndOfAssemblyBaseEndOfAssembly: ClassInitialize + TestClassDerived_EndOfAssemblyEndOfAssembly: TestMethod + TestClassIntermediateEndOfAssemblyBaseEndOfAssembly: ClassCleanup + TestClassBaseEndOfAssembly: ClassCleanup + TestClassBaseEndOfAssembly: ClassCleanup + Console: AssemblyCleanup was called + + """, + testMethod10.Messages[0].Text); } private static string GenerateTraceDebugPrefixedMessage(string message) diff --git a/test/IntegrationTests/MSTest.VstestConsoleWrapper.IntegrationTests/TestProjectFSharpTests.cs b/test/IntegrationTests/MSTest.VstestConsoleWrapper.IntegrationTests/TestProjectFSharpTests.cs index 10e8998336..472dbd5886 100644 --- a/test/IntegrationTests/MSTest.VstestConsoleWrapper.IntegrationTests/TestProjectFSharpTests.cs +++ b/test/IntegrationTests/MSTest.VstestConsoleWrapper.IntegrationTests/TestProjectFSharpTests.cs @@ -5,10 +5,12 @@ namespace MSTest.VstestConsoleWrapper.IntegrationTests; +[TestClass] public class TestProjectFSharpTests : CLITestBase { private const string TestAssetName = "FSharpTestProject"; + [TestMethod] public void ExecuteCustomTestExtensibilityTests() { InvokeVsTestForExecution([TestAssetName], targetFramework: "net472"); diff --git a/test/IntegrationTests/MSTest.VstestConsoleWrapper.IntegrationTests/TimeoutTests.cs b/test/IntegrationTests/MSTest.VstestConsoleWrapper.IntegrationTests/TimeoutTests.cs index b7e513c82b..81358f7b32 100644 --- a/test/IntegrationTests/MSTest.VstestConsoleWrapper.IntegrationTests/TimeoutTests.cs +++ b/test/IntegrationTests/MSTest.VstestConsoleWrapper.IntegrationTests/TimeoutTests.cs @@ -1,21 +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 FluentAssertions; - using Microsoft.MSTestV2.CLIAutomation; namespace MSTest.VstestConsoleWrapper.IntegrationTests; +[TestClass] public class TimeoutTests : CLITestBase { private const string TestAssetName = "TimeoutTestProject"; - public void ValidateTimeoutTests_net462() => ValidateTimeoutTests("net462"); - - public void ValidateTimeoutTests_netcoreapp31() => ValidateTimeoutTests("netcoreapp3.1"); - - private void ValidateTimeoutTests(string targetFramework) + [TestMethod] + [DataRow("net462")] + [DataRow("netcoreapp3.1")] + public void ValidateTimeoutTests(string targetFramework) { InvokeVsTestForExecution([TestAssetName], testCaseFilter: "TimeoutTest|RegularTest", targetFramework: targetFramework); @@ -34,7 +32,7 @@ private void ValidateTimeoutTests(string targetFramework) failedTests.Add("TimeoutTestProject.TimeoutTestClass.TimeoutTest_WhenUserCallsThreadAbort_AbortTest"); } - ValidateFailedTestsContain(false, failedTests.ToArray()); + ValidateFailedTestsContain(false, [.. failedTests]); // We should find the /TimeoutTestOutput.txt file, as it's our way to validate // that when the timeout expires it cancels the test context token. @@ -42,6 +40,6 @@ private void ValidateTimeoutTests(string targetFramework) GetAssetFullPath(TestAssetName, targetFramework: targetFramework), "..", "TimeoutTestOutput.txt"); - File.Exists(timeoutFile).Should().BeTrue(); + Assert.IsTrue(File.Exists(timeoutFile), $"Timeout output file not found: {timeoutFile}"); } } diff --git a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/AbortionTests.cs b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/AbortionTests.cs index e35a768106..b66cb255f3 100644 --- a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/AbortionTests.cs +++ b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/AbortionTests.cs @@ -21,17 +21,13 @@ public async Task AbortWithCTRLPlusC_TestHost_Succeeded(string tfm) } var testHost = TestInfrastructure.TestHost.LocateFrom(AssetFixture.TargetAssetPath, AssetName, tfm); - TestHostResult testHostResult = await testHost.ExecuteAsync(); + TestHostResult testHostResult = await testHost.ExecuteAsync(cancellationToken: TestContext.CancellationToken); testHostResult.AssertExitCodeIs(ExitCodes.TestSessionAborted); - // We check only in netcore for netfx is now showing in CI every time, the same behavior in local something works sometime nope. - // Manual test works pretty always as expected, looks like the implementation is different, we care more on .NET Core. - if (TargetFrameworks.Net.Contains(tfm)) - { - testHostResult.AssertOutputMatchesRegex("Canceling the test session.*"); - } - + // We don't assert "Canceling the test session" message. + // Cancellation could happen very first that we didn't have the opportunity to write this message. + // However, the summary should always be correct and should always indicate that the session was aborted. testHostResult.AssertOutputContainsSummary(failed: 0, passed: 0, skipped: 0, aborted: true); } @@ -163,4 +159,6 @@ internal class Capabilities : ITestFrameworkCapabilities .PatchCodeWithReplace("$MicrosoftTestingPlatformVersion$", MicrosoftTestingPlatformVersion)); } } + + public TestContext TestContext { get; set; } } diff --git a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/ConsoleTests.cs b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/ConsoleTests.cs index 31f4b9b9b4..e035572d0f 100644 --- a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/ConsoleTests.cs +++ b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/ConsoleTests.cs @@ -43,7 +43,7 @@ private async Task ConsoleTestsCoreAsync(string tfm, string? environmentVariable }; } - TestHostResult testHostResult = await testHost.ExecuteAsync("--no-ansi --ignore-exit-code 8", environmentVariables); + TestHostResult testHostResult = await testHost.ExecuteAsync("--ignore-exit-code 8", environmentVariables); testHostResult.AssertExitCodeIs(ExitCodes.Success); testHostResult.AssertOutputContains("ABCDEF123"); } diff --git a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/CrashDumpTests.cs b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/CrashDumpTests.cs index fdb315d1cc..9c9c3e209b 100644 --- a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/CrashDumpTests.cs +++ b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/CrashDumpTests.cs @@ -18,10 +18,10 @@ public async Task CrashDump_DefaultSetting_CreateDump(string tfm) string resultDirectory = Path.Combine(AssetFixture.TargetAssetPath, Guid.NewGuid().ToString("N")); var testHost = TestInfrastructure.TestHost.LocateFrom(AssetFixture.TargetAssetPath, "CrashDump", tfm); - TestHostResult testHostResult = await testHost.ExecuteAsync($"--crashdump --results-directory {resultDirectory}"); + TestHostResult testHostResult = await testHost.ExecuteAsync($"--crashdump --results-directory {resultDirectory}", cancellationToken: TestContext.CancellationToken); testHostResult.AssertExitCodeIs(ExitCodes.TestHostProcessExitedNonGracefully); string? dumpFile = Directory.GetFiles(resultDirectory, "CrashDump_*.dmp", SearchOption.AllDirectories).SingleOrDefault(); - Assert.IsTrue(dumpFile is not null, $"Dump file not found '{tfm}'\n{testHostResult}'"); + Assert.IsNotNull(dumpFile, $"Dump file not found '{tfm}'\n{testHostResult}'"); } [TestMethod] @@ -35,9 +35,9 @@ public async Task CrashDump_CustomDumpName_CreateDump() string resultDirectory = Path.Combine(AssetFixture.TargetAssetPath, Guid.NewGuid().ToString("N")); var testHost = TestInfrastructure.TestHost.LocateFrom(AssetFixture.TargetAssetPath, "CrashDump", TargetFrameworks.NetCurrent); - TestHostResult testHostResult = await testHost.ExecuteAsync($"--crashdump --crashdump-filename customdumpname.dmp --results-directory {resultDirectory}"); + TestHostResult testHostResult = await testHost.ExecuteAsync($"--crashdump --crashdump-filename customdumpname.dmp --results-directory {resultDirectory}", cancellationToken: TestContext.CancellationToken); testHostResult.AssertExitCodeIs(ExitCodes.TestHostProcessExitedNonGracefully); - Assert.IsTrue(Directory.GetFiles(resultDirectory, "customdumpname.dmp", SearchOption.AllDirectories).SingleOrDefault() is not null, "Dump file not found"); + Assert.IsNotNull(Directory.GetFiles(resultDirectory, "customdumpname.dmp", SearchOption.AllDirectories).SingleOrDefault(), "Dump file not found"); } [DataRow("Mini")] @@ -55,10 +55,10 @@ public async Task CrashDump_Formats_CreateDump(string format) string resultDirectory = Path.Combine(AssetFixture.TargetAssetPath, Guid.NewGuid().ToString("N")); var testHost = TestInfrastructure.TestHost.LocateFrom(AssetFixture.TargetAssetPath, "CrashDump", TargetFrameworks.NetCurrent); - TestHostResult testHostResult = await testHost.ExecuteAsync($"--crashdump --crashdump-type {format} --results-directory {resultDirectory}"); + TestHostResult testHostResult = await testHost.ExecuteAsync($"--crashdump --crashdump-type {format} --results-directory {resultDirectory}", cancellationToken: TestContext.CancellationToken); testHostResult.AssertExitCodeIs(ExitCodes.TestHostProcessExitedNonGracefully); string? dumpFile = Directory.GetFiles(resultDirectory, "CrashDump_*.dmp", SearchOption.AllDirectories).SingleOrDefault(); - Assert.IsTrue(dumpFile is not null, $"Dump file not found '{format}'\n{testHostResult}'"); + Assert.IsNotNull(dumpFile, $"Dump file not found '{format}'\n{testHostResult}'"); File.Delete(dumpFile); } @@ -67,7 +67,7 @@ public async Task CrashDump_InvalidFormat_ShouldFail() { string resultDirectory = Path.Combine(AssetFixture.TargetAssetPath, Guid.NewGuid().ToString("N")); var testHost = TestInfrastructure.TestHost.LocateFrom(AssetFixture.TargetAssetPath, "CrashDump", TargetFrameworks.NetCurrent); - TestHostResult testHostResult = await testHost.ExecuteAsync($"--crashdump --crashdump-type invalid --results-directory {resultDirectory}"); + TestHostResult testHostResult = await testHost.ExecuteAsync($"--crashdump --crashdump-type invalid --results-directory {resultDirectory}", cancellationToken: TestContext.CancellationToken); 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'"); } @@ -154,4 +154,6 @@ public Task ExecuteRequestAsync(ExecuteRequestContext context) } """; } + + public TestContext TestContext { get; set; } } diff --git a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/CrashPlusHangDumpTests.cs b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/CrashPlusHangDumpTests.cs index 11f81640bf..010c4160e3 100644 --- a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/CrashPlusHangDumpTests.cs +++ b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/CrashPlusHangDumpTests.cs @@ -24,14 +24,15 @@ public async Task CrashPlusHangDump_InCaseOfCrash_CreateCrashDump() { "SLEEPTIMEMS1", "4000" }, { "SLEEPTIMEMS2", "600000" }, { "SHOULDCRASH", "true" }, - }); + }, + cancellationToken: TestContext.CancellationToken); 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*_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}'"); + Assert.IsGreaterThan(0, Directory.GetFiles(resultDirectory, "CrashPlusHangDump*_crash.dmp", SearchOption.AllDirectories).Length, $"Dump file not found '{TargetFrameworks.NetCurrent}'\n{testHostResult}'"); + Assert.IsLessThanOrEqualTo(0, Directory.GetFiles(resultDirectory, "CrashPlusHangDump*_hang.dmp", SearchOption.AllDirectories).Length, $"Dump file not found '{TargetFrameworks.NetCurrent}'\n{testHostResult}'"); } [TestMethod] @@ -52,14 +53,15 @@ public async Task CrashPlusHangDump_InCaseOfHang_CreateHangDump() { "SLEEPTIMEMS1", "4000" }, { "SLEEPTIMEMS2", "600000" }, { "SHOULDCRASH", "false" }, - }); + }, + cancellationToken: TestContext.CancellationToken); 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}'"); + Assert.IsLessThanOrEqualTo(0, Directory.GetFiles(resultDirectory, "CrashPlusHangDump.dll*_crash.dmp", SearchOption.AllDirectories).Length, $"Dump file not found '{TargetFrameworks.NetCurrent}'\n{testHostResult}'"); + Assert.IsGreaterThan(0, Directory.GetFiles(resultDirectory, "CrashPlusHangDump*_hang.dmp", SearchOption.AllDirectories).Length, $"Dump file not found '{TargetFrameworks.NetCurrent}'\n{testHostResult}'"); } public sealed class TestAssetFixture() : TestAssetFixtureBase(AcceptanceFixture.NuGetGlobalPackagesFolder) @@ -171,4 +173,6 @@ public async Task ExecuteRequestAsync(ExecuteRequestContext context) } """; } + + public TestContext TestContext { get; set; } } diff --git a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/CustomBannerTests.cs b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/CustomBannerTests.cs index de6d1c5cfb..52d363b1e9 100644 --- a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/CustomBannerTests.cs +++ b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/CustomBannerTests.cs @@ -13,7 +13,7 @@ public class CustomBannerTests : AcceptanceTestBase { { "TESTINGPLATFORM_NOBANNER", "true" }, - }); + }, + cancellationToken: TestContext.CancellationToken); testHostResult.AssertExitCodeIs(ExitCodes.ZeroTests); testHostResult.AssertOutputDoesNotContain(TestAssetFixture.CustomBannerPrefix); @@ -45,7 +46,8 @@ public async Task UsingDotnetNoLogo_InTheEnvironmentVars_TheBannerDoesNotAppear( new Dictionary { { "DOTNET_NOLOGO", "true" }, - }); + }, + cancellationToken: TestContext.CancellationToken); testHostResult.AssertExitCodeIs(ExitCodes.ZeroTests); testHostResult.AssertOutputDoesNotContain(TestAssetFixture.CustomBannerPrefix); @@ -56,7 +58,7 @@ public async Task UsingDotnetNoLogo_InTheEnvironmentVars_TheBannerDoesNotAppear( public async Task WithoutUsingNoBanner_TheBannerAppears(string tfm) { var testHost = TestInfrastructure.TestHost.LocateFrom(AssetFixture.TargetAssetPath, AssetName, tfm); - TestHostResult testHostResult = await testHost.ExecuteAsync(); + TestHostResult testHostResult = await testHost.ExecuteAsync(cancellationToken: TestContext.CancellationToken); testHostResult.AssertExitCodeIs(ExitCodes.ZeroTests); testHostResult.AssertOutputMatchesRegex($"{TestAssetFixture.CustomBannerPrefix} Platform info: Name: Microsoft.Testing.Platform, Version: .+?, Hash: .*?, Date: .+?"); @@ -162,4 +164,6 @@ public Task ExecuteRequestAsync(ExecuteRequestContext context) .PatchCodeWithReplace("$MicrosoftTestingPlatformVersion$", MicrosoftTestingPlatformVersion)); } } + + public TestContext TestContext { get; set; } } diff --git a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/DiagnosticTests.cs b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/DiagnosticTests.cs index d0dae9f7c3..a6dceec769 100644 --- a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/DiagnosticTests.cs +++ b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/DiagnosticTests.cs @@ -18,7 +18,7 @@ public async Task Diag_WhenDiagnosticIsSpecified_ReportIsGeneratedInDefaultLocat string diagPathPattern = Path.Combine(diagPath, @"log_.*.diag").Replace(@"\", @"\\"); var testHost = TestInfrastructure.TestHost.LocateFrom(AssetFixture.TargetAssetPath, AssetName, tfm); - TestHostResult testHostResult = await testHost.ExecuteAsync("--diagnostic"); + TestHostResult testHostResult = await testHost.ExecuteAsync("--diagnostic", cancellationToken: TestContext.CancellationToken); await AssertDiagnosticReportWasGeneratedAsync(testHostResult, diagPathPattern); } @@ -31,7 +31,7 @@ public async Task Diag_WhenDiagnosticAndOutputFilePrefixAreSpecified_ReportIsGen string diagPathPattern = Path.Combine(diagPath, @"abcd_.*.diag").Replace(@"\", @"\\"); var testHost = TestInfrastructure.TestHost.LocateFrom(AssetFixture.TargetAssetPath, AssetName, tfm); - TestHostResult testHostResult = await testHost.ExecuteAsync("--diagnostic --diagnostic-output-fileprefix abcd"); + TestHostResult testHostResult = await testHost.ExecuteAsync("--diagnostic --diagnostic-output-fileprefix abcd", cancellationToken: TestContext.CancellationToken); await AssertDiagnosticReportWasGeneratedAsync(testHostResult, diagPathPattern); } @@ -46,7 +46,7 @@ public async Task Diag_WhenDiagnosticAndOutputDirectoryAreSpecified_ReportIsGene Assert.IsTrue(Directory.CreateDirectory(diagPath).Exists); var testHost = TestInfrastructure.TestHost.LocateFrom(AssetFixture.TargetAssetPath, AssetName, tfm); - TestHostResult testHostResult = await testHost.ExecuteAsync($"--diagnostic --diagnostic-output-directory {diagPath}"); + TestHostResult testHostResult = await testHost.ExecuteAsync($"--diagnostic --diagnostic-output-directory {diagPath}", cancellationToken: TestContext.CancellationToken); await AssertDiagnosticReportWasGeneratedAsync(testHostResult, diagPathPattern); } @@ -61,7 +61,7 @@ public async Task Diag_WhenDiagnosticAndOutputFilePrefixAndOutputDirectoryAreSpe Assert.IsTrue(Directory.CreateDirectory(diagPath).Exists); var testHost = TestInfrastructure.TestHost.LocateFrom(AssetFixture.TargetAssetPath, AssetName, tfm); - TestHostResult testHostResult = await testHost.ExecuteAsync($"--diagnostic --diagnostic-output-fileprefix abcde --diagnostic-output-directory {diagPath}"); + TestHostResult testHostResult = await testHost.ExecuteAsync($"--diagnostic --diagnostic-output-fileprefix abcde --diagnostic-output-directory {diagPath}", cancellationToken: TestContext.CancellationToken); await AssertDiagnosticReportWasGeneratedAsync(testHostResult, diagPathPattern); } @@ -71,7 +71,7 @@ public async Task Diag_WhenDiagnosticAndOutputFilePrefixAndOutputDirectoryAreSpe public async Task Diag_WhenDiagnosticOutputFilePrefixButNotDiagnosticIsSpecified_ReportGenerationFails(string tfm) { var testHost = TestInfrastructure.TestHost.LocateFrom(AssetFixture.TargetAssetPath, AssetName, tfm); - TestHostResult testHostResult = await testHost.ExecuteAsync("--diagnostic-output-fileprefix cccc"); + TestHostResult testHostResult = await testHost.ExecuteAsync("--diagnostic-output-fileprefix cccc", cancellationToken: TestContext.CancellationToken); testHostResult.AssertExitCodeIs(ExitCodes.InvalidCommandLine); testHostResult.AssertOutputContains("'--diagnostic-output-fileprefix' requires '--diagnostic' to be provided"); @@ -82,7 +82,7 @@ public async Task Diag_WhenDiagnosticOutputFilePrefixButNotDiagnosticIsSpecified public async Task Diag_WhenDiagnosticOutputDirectoryButNotDiagnosticIsSpecified_ReportGenerationFails(string tfm) { var testHost = TestInfrastructure.TestHost.LocateFrom(AssetFixture.TargetAssetPath, AssetName, tfm); - TestHostResult testHostResult = await testHost.ExecuteAsync("--diagnostic-output-directory cccc"); + TestHostResult testHostResult = await testHost.ExecuteAsync("--diagnostic-output-directory cccc", cancellationToken: TestContext.CancellationToken); testHostResult.AssertExitCodeIs(ExitCodes.InvalidCommandLine); testHostResult.AssertOutputContains("'--diagnostic-output-directory' requires '--diagnostic' to be provided"); @@ -93,7 +93,7 @@ public async Task Diag_WhenDiagnosticOutputDirectoryButNotDiagnosticIsSpecified_ public async Task Diag_WhenDiagnosticFilePrefixAndDiagnosticOutputDirectoryButNotDiagnosticAreSpecified_ReportGenerationFails(string tfm) { var testHost = TestInfrastructure.TestHost.LocateFrom(AssetFixture.TargetAssetPath, AssetName, tfm); - TestHostResult testHostResult = await testHost.ExecuteAsync("--diagnostic-output-fileprefix aaaa --diagnostic-output-directory cccc"); + TestHostResult testHostResult = await testHost.ExecuteAsync("--diagnostic-output-fileprefix aaaa --diagnostic-output-directory cccc", cancellationToken: TestContext.CancellationToken); testHostResult.AssertExitCodeIs(ExitCodes.InvalidCommandLine); testHostResult.AssertOutputContains("'--diagnostic-output-directory' requires '--diagnostic' to be provided"); @@ -112,7 +112,7 @@ public async Task Diag_EnableWithEnvironmentVariables_Succeeded(string tfm) new Dictionary { { EnvironmentVariableConstants.TESTINGPLATFORM_DIAGNOSTIC, "1" }, - }); + }, cancellationToken: TestContext.CancellationToken); await AssertDiagnosticReportWasGeneratedAsync(testHostResult, diagPathPattern); } @@ -131,7 +131,8 @@ public async Task Diag_EnableWithEnvironmentVariables_Verbosity_Succeeded(string { { EnvironmentVariableConstants.TESTINGPLATFORM_DIAGNOSTIC, "1" }, { EnvironmentVariableConstants.TESTINGPLATFORM_DIAGNOSTIC_VERBOSITY, "Trace" }, - }); + }, + cancellationToken: TestContext.CancellationToken); await AssertDiagnosticReportWasGeneratedAsync(testHostResult, diagPathPattern); } @@ -150,7 +151,8 @@ public async Task Diag_EnableWithEnvironmentVariables_CustomPrefix_Succeeded(str { { EnvironmentVariableConstants.TESTINGPLATFORM_DIAGNOSTIC, "1" }, { EnvironmentVariableConstants.TESTINGPLATFORM_DIAGNOSTIC_OUTPUT_FILEPREFIX, "MyPrefix" }, - }); + }, + cancellationToken: TestContext.CancellationToken); await AssertDiagnosticReportWasGeneratedAsync(testHostResult, diagPathPattern); } @@ -169,7 +171,8 @@ public async Task Diag_EnableWithEnvironmentVariables_SynchronousWrite_Succeeded { { EnvironmentVariableConstants.TESTINGPLATFORM_DIAGNOSTIC, "1" }, { EnvironmentVariableConstants.TESTINGPLATFORM_DIAGNOSTIC_FILELOGGER_SYNCHRONOUSWRITE, "1" }, - }); + }, + cancellationToken: TestContext.CancellationToken); await AssertDiagnosticReportWasGeneratedAsync(testHostResult, diagPathPattern, flushType: "sync"); } @@ -185,11 +188,12 @@ public async Task Diag_EnableWithEnvironmentVariables_Disable_Succeeded(string t new Dictionary { { EnvironmentVariableConstants.TESTINGPLATFORM_DIAGNOSTIC, "0" }, - }); + }, + cancellationToken: TestContext.CancellationToken); testHostResult.AssertExitCodeIs(ExitCodes.ZeroTests); testHostResult.AssertOutputDoesNotContain("Diagnostic file"); - testHostResult = await testHost.ExecuteAsync("--diagnostic"); + testHostResult = await testHost.ExecuteAsync("--diagnostic", cancellationToken: TestContext.CancellationToken); testHostResult.AssertExitCodeIs(ExitCodes.ZeroTests); testHostResult.AssertOutputContains("Diagnostic file"); } @@ -296,4 +300,6 @@ public Task ExecuteRequestAsync(ExecuteRequestContext context) .PatchCodeWithReplace("$MicrosoftTestingPlatformVersion$", MicrosoftTestingPlatformVersion)); } } + + public TestContext TestContext { get; set; } } diff --git a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/EnvironmentVariablesConfigurationProviderTests.cs b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/EnvironmentVariablesConfigurationProviderTests.cs index a3c16669da..a715df1c6f 100644 --- a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/EnvironmentVariablesConfigurationProviderTests.cs +++ b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/EnvironmentVariablesConfigurationProviderTests.cs @@ -13,7 +13,7 @@ public sealed class EnvironmentVariablesConfigurationProviderTests : AcceptanceT public async Task SetEnvironmentVariable_ShouldSucceed(string currentTfm) { var testHost = TestInfrastructure.TestHost.LocateFrom(AssetFixture.TargetAssetPath, AssetName, currentTfm); - TestHostResult testHostResult = await testHost.ExecuteAsync(); + TestHostResult testHostResult = await testHost.ExecuteAsync(cancellationToken: TestContext.CancellationToken); testHostResult.AssertExitCodeIs(ExitCodes.Success); } @@ -136,4 +136,6 @@ public async Task ExecuteRequestAsync(ExecuteRequestContext context) .PatchCodeWithReplace("$MicrosoftTestingPlatformVersion$", MicrosoftTestingPlatformVersion)); } } + + public TestContext TestContext { get; set; } } diff --git a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/ExecutionTests.cs b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/ExecutionTests.cs index 0f4857f6e8..b80efbe3e1 100644 --- a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/ExecutionTests.cs +++ b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/ExecutionTests.cs @@ -14,7 +14,7 @@ public class ExecutionTests : AcceptanceTestBase\""); + TestHostResult testHostResult = await testHost.ExecuteAsync("--list-tests --treenode-filter \"\"", cancellationToken: TestContext.CancellationToken); testHostResult.AssertExitCodeIs(ExitCodes.Success); @@ -62,7 +116,7 @@ public async Task Exec_WhenListTestsAndFilterAreSpecified_OnlyFilteredTestsAreFo public async Task Exec_WhenFilterIsSpecified_OnlyFilteredTestsAreRun(string tfm) { var testHost = TestInfrastructure.TestHost.LocateFrom(AssetFixture.TargetAssetPath, AssetName, tfm); - TestHostResult testHostResult = await testHost.ExecuteAsync("--treenode-filter \"\""); + TestHostResult testHostResult = await testHost.ExecuteAsync("--treenode-filter \"\"", cancellationToken: TestContext.CancellationToken); testHostResult.AssertExitCodeIs(ExitCodes.Success); @@ -75,7 +129,7 @@ public async Task Exec_WhenFilterIsSpecified_OnlyFilteredTestsAreRun(string tfm) public async Task Exec_WhenMinimumExpectedTestsIsSpecifiedAndEnoughTestsRun_ResultIsOk(string tfm) { var testHost = TestInfrastructure.TestHost.LocateFrom(AssetFixture.TargetAssetPath, AssetName, tfm); - TestHostResult testHostResult = await testHost.ExecuteAsync("--minimum-expected-tests 2"); + TestHostResult testHostResult = await testHost.ExecuteAsync("--minimum-expected-tests 2", cancellationToken: TestContext.CancellationToken); testHostResult.AssertExitCodeIs(ExitCodes.Success); @@ -88,7 +142,7 @@ public async Task Exec_WhenMinimumExpectedTestsIsSpecifiedAndEnoughTestsRun_Resu public async Task Exec_WhenMinimumExpectedTestsIsSpecifiedAndNotEnoughTestsRun_ResultIsNotOk(string tfm) { var testHost = TestInfrastructure.TestHost.LocateFrom(AssetFixture.TargetAssetPath, AssetName, tfm); - TestHostResult testHostResult = await testHost.ExecuteAsync("--minimum-expected-tests 3"); + TestHostResult testHostResult = await testHost.ExecuteAsync("--minimum-expected-tests 3", cancellationToken: TestContext.CancellationToken); testHostResult.AssertExitCodeIs(ExitCodes.MinimumExpectedTestsPolicyViolation); @@ -101,7 +155,7 @@ public async Task Exec_WhenMinimumExpectedTestsIsSpecifiedAndNotEnoughTestsRun_R public async Task Exec_WhenListTestsAndMinimumExpectedTestsAreSpecified_DiscoveryFails(string tfm) { var testHost = TestInfrastructure.TestHost.LocateFrom(AssetFixture.TargetAssetPath, AssetName, tfm); - TestHostResult testHostResult = await testHost.ExecuteAsync("--list-tests --minimum-expected-tests 2"); + TestHostResult testHostResult = await testHost.ExecuteAsync("--list-tests --minimum-expected-tests 2", cancellationToken: TestContext.CancellationToken); testHostResult.AssertExitCodeIs(ExitCodes.InvalidCommandLine); testHostResult.AssertOutputContains("Error: '--list-tests' and '--minimum-expected-tests' are incompatible options"); @@ -113,10 +167,10 @@ public async Task Exec_Honor_Request_Complete(string tfm) { var testHost = TestInfrastructure.TestHost.LocateFrom(AssetFixture.TargetAssetPath2, AssetName2, tfm); var stopwatch = Stopwatch.StartNew(); - TestHostResult testHostResult = await testHost.ExecuteAsync(); + TestHostResult testHostResult = await testHost.ExecuteAsync(cancellationToken: TestContext.CancellationToken); stopwatch.Stop(); Assert.AreEqual(ExitCodes.Success, testHostResult.ExitCode); - Assert.IsTrue(stopwatch.Elapsed.TotalSeconds > 3); + Assert.IsGreaterThan(3, stopwatch.Elapsed.TotalSeconds); } public sealed class TestAssetFixture() : TestAssetFixtureBase(AcceptanceFixture.NuGetGlobalPackagesFolder) @@ -138,12 +192,14 @@ public sealed class TestAssetFixture() : TestAssetFixtureBase(AcceptanceFixture. #file Program.cs +using System.Linq; using Microsoft.Testing.Platform.Builder; using Microsoft.Testing.Platform.Capabilities.TestFramework; 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.Requests; using Microsoft.Testing.Platform.Services; public class Program @@ -205,17 +261,25 @@ public async Task ExecuteRequestAsync(ExecuteRequestContext context) // Simulate that here. bool isDiscovery = _sp.GetCommandLineOptions().TryGetOptionArgumentList("--list-tests", out _); - - await context.MessageBus.PublishAsync(this, new TestNodeUpdateMessage(context.Request.Session.SessionUid, - new TestNode() { Uid = "0", DisplayName = "Test1", Properties = new(DiscoveredTestNodeStateProperty.CachedInstance) })); + var uidListFilter = ((TestExecutionRequest)context.Request).Filter as TestNodeUidListFilter; + + // If --filter-uid is used, but it doesn't contain a given Uid, then don't publish TestNodeUpdateMessage for that Uid. + var excludeUid0 = uidListFilter is not null && !uidListFilter.TestNodeUids.Any(n => n.Value == "0"); + var excludeUid1 = uidListFilter is not null && !uidListFilter.TestNodeUids.Any(n => n.Value == "1"); + + if (!excludeUid0) + { + await context.MessageBus.PublishAsync(this, new TestNodeUpdateMessage(context.Request.Session.SessionUid, + new TestNode() { Uid = "0", DisplayName = "Test1", Properties = new(DiscoveredTestNodeStateProperty.CachedInstance) })); + } - if (!isDiscovery) + if (!isDiscovery && !excludeUid0) { await context.MessageBus.PublishAsync(this, new TestNodeUpdateMessage(context.Request.Session.SessionUid, new TestNode() { Uid = "0", DisplayName = "Test1", Properties = new(PassedTestNodeStateProperty.CachedInstance) })); } - if (!_sp.GetCommandLineOptions().TryGetOptionArgumentList("--treenode-filter", out _)) + if (!_sp.GetCommandLineOptions().TryGetOptionArgumentList("--treenode-filter", out _) && !excludeUid1) { await context.MessageBus.PublishAsync(this, new TestNodeUpdateMessage(context.Request.Session.SessionUid, new TestNode() { Uid = "1", DisplayName = "Test2", Properties = new(DiscoveredTestNodeStateProperty.CachedInstance) })); @@ -322,4 +386,6 @@ internal class Capabilities : ITestFrameworkCapabilities .PatchCodeWithReplace("$MicrosoftTestingPlatformVersion$", MicrosoftTestingPlatformVersion)); } } + + public TestContext TestContext { get; set; } } diff --git a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/ExitOnProcessExitTests.cs b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/ExitOnProcessExitTests.cs index 8e7ae8bb7f..b14ea8c35f 100644 --- a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/ExitOnProcessExitTests.cs +++ b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/ExitOnProcessExitTests.cs @@ -16,7 +16,7 @@ public void ExitOnProcessExit_Succeed(string tfm) // Create the mutex name used to wait for the PID file created by the test host. string waitPid = Guid.NewGuid().ToString("N"); - _ = testHost.ExecuteAsync(environmentVariables: new Dictionary { { "WaitPid", waitPid } }); + _ = testHost.ExecuteAsync(environmentVariables: new Dictionary { { "WaitPid", waitPid } }, cancellationToken: TestContext.CancellationToken); Process? process; var startTime = Stopwatch.StartNew(); @@ -25,7 +25,7 @@ public void ExitOnProcessExit_Succeed(string tfm) Thread.Sleep(500); // Look for the pid file created by the test host. - string[] pidFile = Directory.GetFiles(Path.GetDirectoryName(testHost.FullName)!, "PID").ToArray(); + string[] pidFile = [.. Directory.GetFiles(Path.GetDirectoryName(testHost.FullName)!, "PID")]; if (pidFile.Length > 0) { string pid = File.ReadAllText(pidFile[0]); @@ -37,7 +37,7 @@ public void ExitOnProcessExit_Succeed(string tfm) } } - if (startTime.Elapsed.TotalSeconds > 55) + if (startTime.Elapsed.TotalSeconds > 60) { throw new Exception("Process PID not found in 60 seconds"); } @@ -48,7 +48,8 @@ public void ExitOnProcessExit_Succeed(string tfm) startTime = Stopwatch.StartNew(); while (!process.HasExited) { - if (startTime.Elapsed.TotalSeconds > 55) + Thread.Sleep(1000); + if (startTime.Elapsed.TotalSeconds > 60) { throw new Exception("Process did not exit in 60 seconds"); } @@ -83,7 +84,7 @@ public sealed class TestAssetFixture() : TestAssetFixtureBase(AcceptanceFixture. using Microsoft.Testing.Platform.Extensions.Messages; using Microsoft.Testing.Platform.Extensions.TestFramework; -if (args.Length == 0) +if (!args.Contains("--exit-on-process-exit")) { int currentPid = Process.GetCurrentProcess().Id; var currentEntryPoint = Path.Combine(Path.GetDirectoryName(Assembly.GetEntryAssembly()!.Location)!, Path.GetFileNameWithoutExtension(Assembly.GetEntryAssembly()!.Location) @@ -93,7 +94,7 @@ public sealed class TestAssetFixture() : TestAssetFixtureBase(AcceptanceFixture. Environment.SetEnvironmentVariable("WaitTestHost", mutexName); ProcessStartInfo processStartInfo = new(); processStartInfo.FileName = currentEntryPoint; - processStartInfo.Arguments = $"--exit-on-process-exit {currentPid}"; + processStartInfo.Arguments = $"--exit-on-process-exit {currentPid} --no-progress --no-ansi"; processStartInfo.UseShellExecute = false; var process = Process.Start(processStartInfo); while (!Mutex.TryOpenExisting(mutexName, out Mutex? _)) @@ -168,4 +169,6 @@ public async Task ExecuteRequestAsync(ExecuteRequestContext context) .PatchCodeWithReplace("$MicrosoftTestingPlatformVersion$", MicrosoftTestingPlatformVersion)); } } + + public TestContext TestContext { get; set; } } diff --git a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/HangDumpOutputTests.cs b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/HangDumpOutputTests.cs index aabb30647e..8992c66746 100644 --- a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/HangDumpOutputTests.cs +++ b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/HangDumpOutputTests.cs @@ -29,7 +29,8 @@ public async Task HangDump_Outputs_HangingTests_EvenWhenHangingTestsHaveTheSameD { { "SLEEPTIMEMS1", "100" }, { "SLEEPTIMEMS2", "600000" }, - }); + }, + cancellationToken: TestContext.CancellationToken); testHostResult.AssertExitCodeIs(ExitCodes.TestHostProcessExitedNonGracefully); testHostResult.AssertOutputContains("Test1"); } @@ -150,4 +151,6 @@ public async Task ExecuteRequestAsync(ExecuteRequestContext context) } """; } + + public TestContext TestContext { get; set; } } diff --git a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/HangDumpProcessTreeTests.cs b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/HangDumpProcessTreeTests.cs new file mode 100644 index 0000000000..83180701dd --- /dev/null +++ b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/HangDumpProcessTreeTests.cs @@ -0,0 +1,172 @@ +// 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.Acceptance.IntegrationTests; + +[TestClass] +public sealed class HangDumpProcessTreeTests : AcceptanceTestBase +{ + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] + [TestMethod] + public async Task HangDump_DumpAllChildProcesses_CreateDump(string tfm) + { + string resultDirectory = Path.Combine(AssetFixture.TargetAssetPath, Guid.NewGuid().ToString("N"), tfm); + var testHost = TestInfrastructure.TestHost.LocateFrom(AssetFixture.TargetAssetPath, "HangDumpWithChild", tfm); + TestHostResult testHostResult = await testHost.ExecuteAsync( + $"--hangdump --hangdump-timeout 8s --hangdump-type mini --results-directory {resultDirectory}", + new Dictionary + { + { "SLEEPTIMEMS1", "4000" }, + { "SLEEPTIMEMS2", "600000" }, + }, + cancellationToken: TestContext.CancellationToken); + testHostResult.AssertExitCodeIs(ExitCodes.TestHostProcessExitedNonGracefully); + string[] dumpFiles = Directory.GetFiles(resultDirectory, "HangDump*.dmp", SearchOption.AllDirectories); + Assert.HasCount(4, dumpFiles, $"There should be 4 dumps, one for each process in the tree. {testHostResult}"); + } + + public sealed class TestAssetFixture() : TestAssetFixtureBase(AcceptanceFixture.NuGetGlobalPackagesFolder) + { + private const string AssetName = "AssetFixture"; + + 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 HangDumpWithChild.csproj + + + + $TargetFrameworks$ + Exe + true + enable + preview + + + + + + + +#file Program.cs +using System; +using System.Linq; +using System.Diagnostics; +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) + { + Process self = Process.GetCurrentProcess(); + string path = self.MainModule!.FileName!; + + if (args[0] == "--child") + { + int child = int.Parse(args[1], CultureInfo.InvariantCulture); + + if (child != 0) + { + var process = Process.Start(new ProcessStartInfo(path, $"--child {child - 1}") + { + UseShellExecute = false, + }); + + // Wait for the child to exit, to make sure we dumping the process in order that will + // end up with multiple dumps. Because typically the parent starts the child and waits for it. + process!.WaitForExit(); + return 0; + } + else + { + // Just sleep for a long time. + Thread.Sleep(3_600_000); + return 0; + } + } + + // We are running under testhost controller, don't start extra processes when we are the controller. + if (args.Any(a => a == "--internal-testhostcontroller-pid")) + { + + Process.Start(new ProcessStartInfo(path, $"--child 2") + { + UseShellExecute = false, + }); + } + + ITestApplicationBuilder builder = await TestApplication.CreateBuilderAsync(args); + builder.RegisterTestFramework(_ => new TestFrameworkCapabilities(), (_,__) => new DummyTestFramework()); + builder.AddHangDumpProvider(); + using ITestApplication app = await builder.BuildAsync(); + return await app.RunAsync(); + } +} + +public class DummyTestFramework : ITestFramework, IDataProducer +{ + public string Uid => nameof(DummyTestFramework); + + public string Version => "2.0.0"; + + public string DisplayName => nameof(DummyTestFramework); + + public string Description => nameof(DummyTestFramework); + + 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) + { + + Thread.Sleep(int.Parse(Environment.GetEnvironmentVariable("SLEEPTIMEMS1")!, CultureInfo.InvariantCulture)); + + 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("SLEEPTIMEMS2")!, CultureInfo.InvariantCulture)); + + await context.MessageBus.PublishAsync(this, new TestNodeUpdateMessage(context.Request.Session.SessionUid, new TestNode() + { + Uid = "Test2", + DisplayName = "Test2", + Properties = new PropertyBag(new PassedTestNodeStateProperty()), + })); + + context.Complete(); + } +} +"""; + } + + public TestContext TestContext { get; set; } +} diff --git a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/HangDumpTests.cs b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/HangDumpTests.cs index 44150dadf0..8e113826ef 100644 --- a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/HangDumpTests.cs +++ b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/HangDumpTests.cs @@ -24,10 +24,11 @@ public async Task HangDump_DefaultSetting_CreateDump(string tfm) { { "SLEEPTIMEMS1", "4000" }, { "SLEEPTIMEMS2", "600000" }, - }); + }, + cancellationToken: TestContext.CancellationToken); 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}'"); + Assert.IsNotNull(dumpFile, $"Dump file not found '{tfm}'\n{testHostResult}'"); } [TestMethod] @@ -47,10 +48,10 @@ public async Task HangDump_CustomFileName_CreateDump() { { "SLEEPTIMEMS1", "4000" }, { "SLEEPTIMEMS2", "600000" }, - }); + }, cancellationToken: TestContext.CancellationToken); 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}'"); + Assert.IsNotNull(dumpFile, $"Dump file not found '{TargetFrameworks.NetCurrent}'\n{testHostResult}'"); } [TestMethod] @@ -72,10 +73,11 @@ public async Task HangDump_PathWithSpaces_CreateDump() { { "SLEEPTIMEMS1", "4000" }, { "SLEEPTIMEMS2", "20000" }, - }); + }, + cancellationToken: TestContext.CancellationToken); 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}'"); + Assert.IsNotNull(dumpFile, $"Dump file not found '{TargetFrameworks.NetCurrent}'\n{testHostResult}'"); } [DataRow("Mini")] @@ -97,12 +99,13 @@ public async Task HangDump_Formats_CreateDump(string format) $"--hangdump --hangdump-timeout 8s --hangdump-type {format} --results-directory {resultDirectory}", new Dictionary { - { "SLEEPTIMEMS1", "4000" }, - { "SLEEPTIMEMS2", "600000" }, - }); + { "SLEEPTIMEMS1", "4000" }, + { "SLEEPTIMEMS2", "600000" }, + }, + cancellationToken: TestContext.CancellationToken); 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}'"); + Assert.IsNotNull(dumpFile, $"Dump file not found '{format}'\n{testHostResult}'"); } [TestMethod] @@ -116,7 +119,8 @@ public async Task HangDump_InvalidFormat_ShouldFail() { { "SLEEPTIMEMS1", "4000" }, { "SLEEPTIMEMS2", "600000" }, - }); + }, + cancellationToken: TestContext.CancellationToken); testHostResult.AssertExitCodeIs(ExitCodes.InvalidCommandLine); testHostResult.AssertOutputContains(""" Option '--hangdump-type' has invalid arguments: 'invalid' is not a valid dump type. @@ -227,4 +231,6 @@ public async Task ExecuteRequestAsync(ExecuteRequestContext context) } """; } + + public TestContext TestContext { get; set; } } diff --git a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/HelpInfoTests.cs b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/HelpInfoTests.cs index f4b19b2e50..20a68cbf65 100644 --- a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/HelpInfoTests.cs +++ b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/HelpInfoTests.cs @@ -11,7 +11,7 @@ public class HelpInfoTests : AcceptanceTestBase public async Task Help_WhenNoExtensionRegistered_OutputDefaultHelpContent(string tfm) { var testHost = TestInfrastructure.TestHost.LocateFrom(AssetFixture.NoExtensionTargetAssetPath, TestAssetFixture.NoExtensionAssetName, tfm); - TestHostResult testHostResult = await testHost.ExecuteAsync("--help"); + TestHostResult testHostResult = await testHost.ExecuteAsync("--help", cancellationToken: TestContext.CancellationToken); testHostResult.AssertExitCodeIs(ExitCodes.Success); @@ -22,6 +22,8 @@ Execute a .NET Test Application. Options: --config-file Specifies a testconfig.json file. + --debug + Allows to pause execution in order to attach to the process for debug purposes. --diagnostic Enable the diagnostic logging. The default log level is 'Trace'. The file will be written in the output directory with the name log_[yyMMddHHmmssfff].diag @@ -39,6 +41,8 @@ Output directory of the diagnostic logging. 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. + --filter-uid + Provides a list of test node UIDs to filter by. --help Show the command line help. --ignore-exit-code @@ -75,7 +79,7 @@ Output verbosity when reporting tests. public async Task HelpShortName_WhenNoExtensionRegistered_OutputDefaultHelpContent(string tfm) { var testHost = TestInfrastructure.TestHost.LocateFrom(AssetFixture.NoExtensionTargetAssetPath, TestAssetFixture.NoExtensionAssetName, tfm); - TestHostResult testHostResult = await testHost.ExecuteAsync("--?"); + TestHostResult testHostResult = await testHost.ExecuteAsync("--?", cancellationToken: TestContext.CancellationToken); testHostResult.AssertExitCodeIs(ExitCodes.Success); @@ -96,7 +100,7 @@ public async Task Help_WhenNoExtensionRegisteredAndUnknownOptionIsSpecified_Outp const string UnknownOption = "aaa"; var testHost = TestInfrastructure.TestHost.LocateFrom(AssetFixture.NoExtensionTargetAssetPath, TestAssetFixture.NoExtensionAssetName, tfm); - TestHostResult testHostResult = await testHost.ExecuteAsync($"-{UnknownOption}"); + TestHostResult testHostResult = await testHost.ExecuteAsync($"-{UnknownOption}", cancellationToken: TestContext.CancellationToken); testHostResult.AssertExitCodeIs(ExitCodes.InvalidCommandLine); @@ -116,7 +120,7 @@ Execute a .NET Test Application. public async Task Info_WhenNoExtensionRegistered_OutputDefaultInfoContent(string tfm) { var testHost = TestInfrastructure.TestHost.LocateFrom(AssetFixture.NoExtensionTargetAssetPath, TestAssetFixture.NoExtensionAssetName, tfm); - TestHostResult testHostResult = await testHost.ExecuteAsync("--info"); + TestHostResult testHostResult = await testHost.ExecuteAsync("--info", cancellationToken: TestContext.CancellationToken); testHostResult.AssertExitCodeIs(ExitCodes.Success); @@ -146,10 +150,14 @@ Microsoft.Testing.Platform v.+ \[.+\] Arity: 1 Hidden: True Description: Specify the port of the client\. - --config-file + --config-file Arity: 1 Hidden: False Description: Specifies a testconfig\.json file\. + --debug + Arity: 0 + Hidden: False + Description: Allows to pause execution in order to attach to the process for debug purposes. --diagnostic Arity: 0 Hidden: False @@ -183,6 +191,10 @@ Note that this is slowing down the test execution\. Arity: 1 Hidden: False Description: Exit the test process if dependent process exits\. PID must be provided\. + --filter-uid + Arity: 1\.\.N + Hidden: False + Description: Provides a list of test node UIDs to filter by\. --help Arity: 0 Hidden: False @@ -262,7 +274,7 @@ There are no registered tools\. public async Task Help_WithAllExtensionsRegistered_OutputFullHelpContent(string tfm) { var testHost = TestInfrastructure.TestHost.LocateFrom(AssetFixture.AllExtensionsTargetAssetPath, TestAssetFixture.AllExtensionsAssetName, tfm); - TestHostResult testHostResult = await testHost.ExecuteAsync("--help"); + TestHostResult testHostResult = await testHost.ExecuteAsync("--help", cancellationToken: TestContext.CancellationToken); testHostResult.AssertExitCodeIs(ExitCodes.Success); @@ -273,6 +285,8 @@ Execute a .NET Test Application. Options: --config-file Specifies a testconfig.json file. + --debug + Allows to pause execution in order to attach to the process for debug purposes. --diagnostic Enable the diagnostic logging. The default log level is 'Trace'. The file will be written in the output directory with the name log_[yyMMddHHmmssfff].diag @@ -290,6 +304,8 @@ Output directory of the diagnostic logging. 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. + --filter-uid + Provides a list of test node UIDs to filter by. --help Show the command line help. --ignore-exit-code @@ -359,7 +375,7 @@ The name of the generated TRX report public async Task HelpShortName_WithAllExtensionsRegistered_OutputFullHelpContent(string tfm) { var testHost = TestInfrastructure.TestHost.LocateFrom(AssetFixture.AllExtensionsTargetAssetPath, TestAssetFixture.AllExtensionsAssetName, tfm); - TestHostResult testHostResult = await testHost.ExecuteAsync("-?"); + TestHostResult testHostResult = await testHost.ExecuteAsync("-?", cancellationToken: TestContext.CancellationToken); testHostResult.AssertExitCodeIs(ExitCodes.Success); @@ -378,7 +394,7 @@ Execute a .NET Test Application. public async Task Info_WithAllExtensionsRegistered_OutputFullInfoContent(string tfm) { var testHost = TestInfrastructure.TestHost.LocateFrom(AssetFixture.AllExtensionsTargetAssetPath, TestAssetFixture.AllExtensionsAssetName, tfm); - TestHostResult testHostResult = await testHost.ExecuteAsync("--info"); + TestHostResult testHostResult = await testHost.ExecuteAsync("--info", cancellationToken: TestContext.CancellationToken); testHostResult.AssertExitCodeIs(ExitCodes.Success); @@ -412,6 +428,10 @@ Microsoft.Testing.Platform v* [*] Arity: 1 Hidden: False Description: Specifies a testconfig.json file. + --debug + Arity: 0 + Hidden: False + Description: Allows to pause execution in order to attach to the process for debug purposes. --diagnostic Arity: 0 Hidden: False @@ -445,6 +465,10 @@ Note that this is slowing down the test execution. Arity: 1 Hidden: False Description: Exit the test process if dependent process exits. PID must be provided. + --filter-uid + Arity: 1..N + Hidden: False + Description: Provides a list of test node UIDs to filter by. --help Arity: 0 Hidden: False @@ -627,6 +651,72 @@ Default type is 'Full' testHostResult.AssertOutputMatchesLines(wildcardPattern); } + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] + [TestMethod] + public async Task Help_DoesNotCreateTestResultsFolder(string tfm) + { + var testHost = TestInfrastructure.TestHost.LocateFrom(AssetFixture.NoExtensionTargetAssetPath, TestAssetFixture.NoExtensionAssetName, tfm); + string testHostDirectory = testHost.DirectoryName; + string testResultsPath = Path.Combine(testHostDirectory, "TestResults"); + + // Ensure TestResults folder doesn't exist before running the test + if (Directory.Exists(testResultsPath)) + { + Directory.Delete(testResultsPath, recursive: true); + } + + TestHostResult testHostResult = await testHost.ExecuteAsync("--help", cancellationToken: TestContext.CancellationToken); + + testHostResult.AssertExitCodeIs(ExitCodes.Success); + + // Verify that TestResults folder was not created + Assert.IsFalse(Directory.Exists(testResultsPath), "TestResults folder should not be created for help command"); + } + + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] + [TestMethod] + public async Task HelpShortName_DoesNotCreateTestResultsFolder(string tfm) + { + var testHost = TestInfrastructure.TestHost.LocateFrom(AssetFixture.NoExtensionTargetAssetPath, TestAssetFixture.NoExtensionAssetName, tfm); + string testHostDirectory = testHost.DirectoryName; + string testResultsPath = Path.Combine(testHostDirectory, "TestResults"); + + // Ensure TestResults folder doesn't exist before running the test + if (Directory.Exists(testResultsPath)) + { + Directory.Delete(testResultsPath, recursive: true); + } + + TestHostResult testHostResult = await testHost.ExecuteAsync("--?", cancellationToken: TestContext.CancellationToken); + + testHostResult.AssertExitCodeIs(ExitCodes.Success); + + // Verify that TestResults folder was not created + Assert.IsFalse(Directory.Exists(testResultsPath), "TestResults folder should not be created for help short name command"); + } + + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] + [TestMethod] + public async Task Info_DoesNotCreateTestResultsFolder(string tfm) + { + var testHost = TestInfrastructure.TestHost.LocateFrom(AssetFixture.NoExtensionTargetAssetPath, TestAssetFixture.NoExtensionAssetName, tfm); + string testHostDirectory = testHost.DirectoryName; + string testResultsPath = Path.Combine(testHostDirectory, "TestResults"); + + // Ensure TestResults folder doesn't exist before running the test + if (Directory.Exists(testResultsPath)) + { + Directory.Delete(testResultsPath, recursive: true); + } + + TestHostResult testHostResult = await testHost.ExecuteAsync("--info", cancellationToken: TestContext.CancellationToken); + + testHostResult.AssertExitCodeIs(ExitCodes.Success); + + // Verify that TestResults folder was not created + Assert.IsFalse(Directory.Exists(testResultsPath), "TestResults folder should not be created for info command"); + } + public sealed class TestAssetFixture() : TestAssetFixtureBase(AcceptanceFixture.NuGetGlobalPackagesFolder) { public const string AllExtensionsAssetName = "AllExtensionsInfoTest"; @@ -773,4 +863,6 @@ public Task ExecuteRequestAsync(ExecuteRequestContext context) .PatchCodeWithReplace("$MicrosoftTestingPlatformVersion$", MicrosoftTestingPlatformVersion)); } } + + public TestContext TestContext { get; set; } } 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 4831e0ae1e..88973c7c5d 100644 --- a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/Helpers/AcceptanceAssert.cs +++ b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/Helpers/AcceptanceAssert.cs @@ -85,10 +85,16 @@ public static void AssertOutputDoesNotMatchRegex(this TestHostResult testHostRes => Assert.IsFalse(Regex.IsMatch(testHostResult.StandardOutput, pattern), GenerateFailedAssertionMessage(testHostResult, callerMemberName: callerMemberName, callerFilePath: callerFilePath, callerLineNumber: callerLineNumber)); public static void AssertOutputContains(this TestHostResult testHostResult, string value, [CallerMemberName] string? callerMemberName = null, [CallerFilePath] string? callerFilePath = null, [CallerLineNumber] int callerLineNumber = 0) - => StringAssert.Contains(testHostResult.StandardOutput, value, GenerateFailedAssertionMessage(testHostResult, callerMemberName: callerMemberName, callerFilePath: callerFilePath, callerLineNumber: callerLineNumber), StringComparison.Ordinal); + => Assert.Contains(value, testHostResult.StandardOutput, StringComparison.Ordinal, GenerateFailedAssertionMessage(testHostResult, callerMemberName: callerMemberName, callerFilePath: callerFilePath, callerLineNumber: callerLineNumber)); + + public static void AssertExitCodeIs(this DotnetMuxerResult testHostResult, int exitCode, [CallerMemberName] string? callerMemberName = null, [CallerFilePath] string? callerFilePath = null, [CallerLineNumber] int callerLineNumber = 0) + => Assert.AreEqual(exitCode, testHostResult.ExitCode, GenerateFailedAssertionMessage(testHostResult, callerMemberName: callerMemberName, callerFilePath: callerFilePath, callerLineNumber: callerLineNumber)); + + public static void AssertExitCodeIsNot(this DotnetMuxerResult testHostResult, int exitCode, [CallerMemberName] string? callerMemberName = null, [CallerFilePath] string? callerFilePath = null, [CallerLineNumber] int callerLineNumber = 0) + => Assert.AreNotEqual(exitCode, testHostResult.ExitCode, GenerateFailedAssertionMessage(testHostResult, callerMemberName: callerMemberName, callerFilePath: callerFilePath, callerLineNumber: callerLineNumber)); public static void AssertOutputContains(this DotnetMuxerResult dotnetMuxerResult, string value, [CallerMemberName] string? callerMemberName = null, [CallerFilePath] string? callerFilePath = null, [CallerLineNumber] int callerLineNumber = 0) - => StringAssert.Contains(dotnetMuxerResult.StandardOutput, value, GenerateFailedAssertionMessage(dotnetMuxerResult, callerMemberName: callerMemberName, callerFilePath: callerFilePath, callerLineNumber: callerLineNumber), StringComparison.Ordinal); + => Assert.Contains(value, dotnetMuxerResult.StandardOutput, StringComparison.Ordinal, GenerateFailedAssertionMessage(dotnetMuxerResult, callerMemberName: callerMemberName, callerFilePath: callerFilePath, callerLineNumber: callerLineNumber)); public static void AssertOutputDoesNotContain(this DotnetMuxerResult dotnetMuxerResult, string value, [CallerMemberName] string? callerMemberName = null, [CallerFilePath] string? callerFilePath = null, [CallerLineNumber] int callerLineNumber = 0) => Assert.IsFalse(dotnetMuxerResult.StandardOutput.Contains(value, StringComparison.Ordinal), GenerateFailedAssertionMessage(dotnetMuxerResult, callerMemberName: callerMemberName, callerFilePath: callerFilePath, callerLineNumber: callerLineNumber)); @@ -100,7 +106,7 @@ public static void AssertOutputDoesNotContain(this TestHostResult testHostResult => Assert.IsFalse(testHostResult.StandardOutput.Contains(value, StringComparison.Ordinal), GenerateFailedAssertionMessage(testHostResult, callerMemberName: callerMemberName, callerFilePath: callerFilePath, callerLineNumber: callerLineNumber)); public static void AssertStandardErrorContains(this TestHostResult testHostResult, string value, [CallerMemberName] string? callerMemberName = null, [CallerFilePath] string? callerFilePath = null, [CallerLineNumber] int callerLineNumber = 0) - => StringAssert.Contains(testHostResult.StandardError, value, GenerateFailedAssertionMessage(testHostResult, callerMemberName: callerMemberName, callerFilePath: callerFilePath, callerLineNumber: callerLineNumber), StringComparison.Ordinal); + => Assert.Contains(value, testHostResult.StandardError, StringComparison.Ordinal, GenerateFailedAssertionMessage(testHostResult, callerMemberName: callerMemberName, callerFilePath: callerFilePath, callerLineNumber: callerLineNumber)); public static void AssertOutputContainsSummary(this TestHostResult testHostResult, int failed, int passed, int skipped, bool? aborted = false, int? minimumNumberOfTests = null, [CallerMemberName] string? callerMemberName = null, [CallerFilePath] string? callerFilePath = null, [CallerLineNumber] int callerLineNumber = 0) { 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 2c3afc4387..1c258e1099 100644 --- a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/Helpers/AcceptanceTestBase.cs +++ b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/Helpers/AcceptanceTestBase.cs @@ -43,9 +43,12 @@ public void TestMethod1() } } -#file dotnet.config -[dotnet.test.runner] -name= "VSTest" +#file global.json +{ + "test": { + "runner": "VSTest" + } +} """; static AcceptanceTestBase() @@ -82,7 +85,7 @@ static AcceptanceTestBase() public static async Task ClassInitialize(TestContext testContext) { AssetFixture = new(); - await AssetFixture.InitializeAsync(); + await AssetFixture.InitializeAsync(testContext.CancellationToken); } [ClassCleanup(InheritanceBehavior.BeforeEachDerivedClass)] @@ -145,17 +148,17 @@ public static void ClassCleanup() } // https://github.com/NuGet/NuGet.Client/blob/c5934bdcbc578eec1e2921f49e6a5d53481c5099/test/NuGet.Core.FuncTests/Msbuild.Integration.Test/MsbuildIntegrationTestFixture.cs#L65-L94 - private protected static async Task FindMsbuildWithVsWhereAsync() + private protected static async Task FindMsbuildWithVsWhereAsync(CancellationToken cancellationToken) { string vswherePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86), "Microsoft Visual Studio", "Installer", "vswhere.exe"); var commandLine = new TestInfrastructure.CommandLine(); - await commandLine.RunAsync($"\"{vswherePath}\" -latest -prerelease -requires Microsoft.Component.MSBuild -find MSBuild\\**\\Bin\\MSBuild.exe"); + await commandLine.RunAsync($"\"{vswherePath}\" -latest -prerelease -requires Microsoft.Component.MSBuild -find MSBuild\\**\\Bin\\MSBuild.exe", cancellationToken: cancellationToken); string? path = null; using (var stringReader = new StringReader(commandLine.StandardOutput)) { string? line; - while ((line = await stringReader.ReadLineAsync()) != null) + while ((line = await stringReader.ReadLineAsync(cancellationToken)) != null) { if (path != null) { @@ -180,14 +183,13 @@ private static string ExtractVersionFromPackage(string rootFolder, string packag // Microsoft.Testing.Platform.Extensions.1.0.0.nupkg // 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 + 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(); + .Select(tuple => tuple.path)]; } if (matches.Length != 1) diff --git a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/IgnoreExitCodeTests.cs b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/IgnoreExitCodeTests.cs index 56551db7b1..17ca19b00d 100644 --- a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/IgnoreExitCodeTests.cs +++ b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/IgnoreExitCodeTests.cs @@ -100,16 +100,19 @@ public async Task If_IgnoreExitCode_Specified_Should_Return_Success_ExitCode(str 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); + await DotnetCli.RunAsync($"restore -m:1 -nodeReuse:false {assetPath} -r {RID}", globalPackagesPath, cancellationToken: TestContext.CancellationToken); + await DotnetCli.RunAsync($"build -m:1 -nodeReuse:false {assetPath} -c {buildConfiguration} -r {RID}", globalPackagesPath, cancellationToken: TestContext.CancellationToken); var host = TestInfrastructure.TestHost.LocateFrom(assetPath, AssetName, tfm, buildConfiguration: buildConfiguration); TestHostResult hostResult = await host.ExecuteAsync( command: commandLine, environmentVariables: new Dictionary { { EnvironmentVariableConstants.TESTINGPLATFORM_EXITCODE_IGNORE, environmentVariable }, - }); + }, + cancellationToken: TestContext.CancellationToken); hostResult.AssertOutputContainsSummary(failed: 1, passed: 0, skipped: 0); Assert.AreEqual(0, hostResult.ExitCode); } + + public TestContext TestContext { get; set; } } diff --git a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/MSBuild.KnownExtensionRegistration.cs b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/MSBuild.KnownExtensionRegistration.cs index 72c7d84f37..597776a966 100644 --- a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/MSBuild.KnownExtensionRegistration.cs +++ b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/MSBuild.KnownExtensionRegistration.cs @@ -19,12 +19,12 @@ public async Task Microsoft_Testing_Platform_Extensions_ShouldBe_Correctly_Regis SourceCode .PatchCodeWithReplace("$TargetFrameworks$", tfm) .PatchCodeWithReplace("$MicrosoftTestingPlatformVersion$", MicrosoftTestingPlatformVersion)); - await DotnetCli.RunAsync($"restore -r {RID} {testAsset.TargetAssetPath}{Path.DirectorySeparatorChar}MSBuildTests.csproj", AcceptanceFixture.NuGetGlobalPackagesFolder.Path); - DotnetMuxerResult result = await DotnetCli.RunAsync($"{(verb == Verb.publish ? $"publish -f {tfm}" : "build")} -c {compilationMode} -r {RID} -nodeReuse:false {testAsset.TargetAssetPath} -v:n", AcceptanceFixture.NuGetGlobalPackagesFolder.Path); + await DotnetCli.RunAsync($"restore -r {RID} {testAsset.TargetAssetPath}{Path.DirectorySeparatorChar}MSBuildTests.csproj", AcceptanceFixture.NuGetGlobalPackagesFolder.Path, cancellationToken: TestContext.CancellationToken); + DotnetMuxerResult result = await DotnetCli.RunAsync($"{(verb == Verb.publish ? $"publish -f {tfm}" : "build")} -c {compilationMode} -r {RID} -nodeReuse:false {testAsset.TargetAssetPath} -v:n", AcceptanceFixture.NuGetGlobalPackagesFolder.Path, cancellationToken: TestContext.CancellationToken); string binlogFile = result.BinlogPath!; var testHost = TestInfrastructure.TestHost.LocateFrom(testAsset.TargetAssetPath, AssetName, tfm, rid: RID, verb: verb, buildConfiguration: compilationMode); - TestHostResult testHostResult = await testHost.ExecuteAsync("--help"); + TestHostResult testHostResult = await testHost.ExecuteAsync("--help", cancellationToken: TestContext.CancellationToken); testHostResult.AssertOutputContains("--crashdump"); testHostResult.AssertOutputContains("--report-trx"); testHostResult.AssertOutputContains("--retry-failed-tests"); @@ -35,12 +35,12 @@ public async Task Microsoft_Testing_Platform_Extensions_ShouldBe_Correctly_Regis SL.Task testingPlatformSelfRegisteredExtensions = generateSelfRegisteredExtensions.FindChildrenRecursive().Single(t => t.Name == "TestingPlatformSelfRegisteredExtensions"); SL.Message generatedSource = testingPlatformSelfRegisteredExtensions.FindChildrenRecursive().Single(m => m.Text.Contains("SelfRegisteredExtensions 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); + Assert.Contains("Microsoft.Testing.Extensions.CrashDump.TestingPlatformBuilderHook.AddExtensions", generatedSource.Text, generatedSource.Text); + Assert.Contains("Microsoft.Testing.Extensions.HangDump.TestingPlatformBuilderHook.AddExtensions", generatedSource.Text, generatedSource.Text); + Assert.Contains("Microsoft.Testing.Extensions.HotReload.TestingPlatformBuilderHook.AddExtensions", generatedSource.Text, generatedSource.Text); + Assert.Contains("Microsoft.Testing.Extensions.Retry.TestingPlatformBuilderHook.AddExtensions", generatedSource.Text, generatedSource.Text); + Assert.Contains("Microsoft.Testing.Extensions.Telemetry.TestingPlatformBuilderHook.AddExtensions", generatedSource.Text, generatedSource.Text); + Assert.Contains("Microsoft.Testing.Extensions.TrxReport.TestingPlatformBuilderHook.AddExtensions", generatedSource.Text, generatedSource.Text); } private const string SourceCode = """ @@ -136,4 +136,6 @@ namespace MSBuildTests.Microsoft; // Then, without global::, Microsoft will be referring to MSBuildTests.Microsoft namespace and will fail to compile public static class DummyClass { } """; + + public TestContext TestContext { get; set; } } diff --git a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/MSBuildTests.ConfigurationFile.cs b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/MSBuildTests.ConfigurationFile.cs index e99e1c7c28..1e84560bcb 100644 --- a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/MSBuildTests.ConfigurationFile.cs +++ b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/MSBuildTests.ConfigurationFile.cs @@ -17,25 +17,26 @@ public async Task ConfigFileGeneration_CorrectlyCreateAndCacheAndCleaned(string .PatchCodeWithReplace("$JsonContent$", ConfigurationContent) .PatchCodeWithReplace("$MicrosoftTestingPlatformVersion$", MicrosoftTestingPlatformVersion)); - DotnetMuxerResult compilationResult = await DotnetCli.RunAsync($"{(verb == Verb.publish ? $"publish -f {tfm}" : "build")} -v:normal -nodeReuse:false {testAsset.TargetAssetPath} -c {compilationMode}", AcceptanceFixture.NuGetGlobalPackagesFolder.Path); + DotnetMuxerResult compilationResult = await DotnetCli.RunAsync($"{(verb == Verb.publish ? $"publish -f {tfm}" : "build")} -v:normal -nodeReuse:false {testAsset.TargetAssetPath} -c {compilationMode}", AcceptanceFixture.NuGetGlobalPackagesFolder.Path, cancellationToken: TestContext.CancellationToken); var testHost = TestInfrastructure.TestHost.LocateFrom(testAsset.TargetAssetPath, "MSBuildTests", tfm, verb: verb, buildConfiguration: compilationMode); string generatedConfigurationFile = Path.Combine(testHost.DirectoryName, "MSBuildTests.testconfig.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")); + Assert.Contains("Microsoft Testing Platform configuration file written", compilationResult.StandardOutput); - compilationResult = await DotnetCli.RunAsync($"{(verb == Verb.publish ? $"publish -f {tfm}" : "build")} -v:normal -nodeReuse:false {testAsset.TargetAssetPath} -c {compilationMode}", AcceptanceFixture.NuGetGlobalPackagesFolder.Path); + compilationResult = await DotnetCli.RunAsync($"{(verb == Verb.publish ? $"publish -f {tfm}" : "build")} -v:normal -nodeReuse:false {testAsset.TargetAssetPath} -c {compilationMode}", AcceptanceFixture.NuGetGlobalPackagesFolder.Path, cancellationToken: TestContext.CancellationToken); Assert.IsTrue(File.Exists(generatedConfigurationFile)); Assert.AreEqual(ConfigurationContent.Trim(), File.ReadAllText(generatedConfigurationFile).Trim()); - compilationResult.StandardOutput.Contains("Microsoft Testing Platform configuration file written"); + // Assert is failing, probably the MSBuild regression which is being fixed in https://github.com/dotnet/msbuild/pull/12431 ? + // compilationResult.AssertOutputContains("Microsoft Testing Platform configuration file written"); Assert.IsTrue(Regex.IsMatch( compilationResult.StandardOutput, """ \s*_GenerateTestingPlatformConfigurationFileCore: \s*Skipping target "_GenerateTestingPlatformConfigurationFileCore" because all output files are up\-to\-date with respect to the input files\. """)); - await DotnetCli.RunAsync($"clean -c {compilationMode} -v:normal {testAsset.TargetAssetPath}", AcceptanceFixture.NuGetGlobalPackagesFolder.Path); + await DotnetCli.RunAsync($"clean -c {compilationMode} -v:normal {testAsset.TargetAssetPath}", AcceptanceFixture.NuGetGlobalPackagesFolder.Path, cancellationToken: TestContext.CancellationToken); // dotnet clean doesn't clean the publish output folder if (verb == Verb.build) @@ -57,10 +58,16 @@ public async Task ConfigFileGeneration_NoConfigurationFile_TaskWontRun(string tf File.Delete(Path.Combine(testAsset.TargetAssetPath, "testconfig.json")); - DotnetMuxerResult compilationResult = await DotnetCli.RunAsync($"{(verb == Verb.publish ? $"publish -f {tfm}" : "build")} -v:diagnostic -nodeReuse:false {testAsset.TargetAssetPath} -c {compilationMode}", AcceptanceFixture.NuGetGlobalPackagesFolder.Path); + DotnetMuxerResult compilationResult = await DotnetCli.RunAsync($"{(verb == Verb.publish ? $"publish -f {tfm}" : "build")} -v:diagnostic -nodeReuse:false {testAsset.TargetAssetPath} -c {compilationMode}", AcceptanceFixture.NuGetGlobalPackagesFolder.Path, cancellationToken: TestContext.CancellationToken); var testHost = TestInfrastructure.TestHost.LocateFrom(testAsset.TargetAssetPath, "MSBuildTests", tfm, verb: verb, buildConfiguration: compilationMode); - Assert.Contains("Target \"_GenerateTestingPlatformConfigurationFileCore\" skipped, due to false condition;", compilationResult.StandardOutput); + + // Working around MSBuild regression: waiting for fix https://github.com/dotnet/msbuild/pull/12431 + // After we insert a new SDK version that ships with a working MSBuild, the DoesNotContain assert will fail. + // Then, remove the DoesNotContain line, and uncomment the Contains line. + // Assert.Contains("Target \"_GenerateTestingPlatformConfigurationFileCore\" skipped, due to false condition;", compilationResult.StandardOutput); + Assert.DoesNotContain("_GenerateTestingPlatformConfigurationFileCore", compilationResult.StandardOutput); + string generatedConfigurationFile = Path.Combine(testHost.DirectoryName, "MSBuildTests.testconfig.json"); Assert.IsFalse(File.Exists(generatedConfigurationFile)); } @@ -135,4 +142,6 @@ public Task ExecuteRequestAsync(ExecuteRequestContext context) } } """; + + public TestContext TestContext { get; set; } } diff --git a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/MSBuildTests.GenerateEntryPoint.cs b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/MSBuildTests.GenerateEntryPoint.cs index 50dbb77d3d..3f86e9ea09 100644 --- a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/MSBuildTests.GenerateEntryPoint.cs +++ b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/MSBuildTests.GenerateEntryPoint.cs @@ -19,14 +19,28 @@ public async Task When_GenerateTestingPlatformEntryPoint_IsFalse_NoEntryPointInj CSharpSourceCode .PatchCodeWithReplace("$TargetFrameworks$", tfm) .PatchCodeWithReplace("$MicrosoftTestingPlatformVersion$", MicrosoftTestingPlatformVersion)); - 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 -p:GenerateTestingPlatformEntryPoint=False {testAsset.TargetAssetPath} -v:n", AcceptanceFixture.NuGetGlobalPackagesFolder.Path, failIfReturnValueIsNotZero: false); + DotnetMuxerResult compilationResult = await DotnetCli.RunAsync($"restore -r {RID} {testAsset.TargetAssetPath}{Path.DirectorySeparatorChar}MSBuildTests.csproj ", AcceptanceFixture.NuGetGlobalPackagesFolder.Path, cancellationToken: TestContext.CancellationToken); + compilationResult = await DotnetCli.RunAsync( + $"{(verb == Verb.publish ? $"publish -f {tfm}" : "build")} -c {compilationMode} -r {RID} -nodeReuse:false -p:GenerateTestingPlatformEntryPoint=False {testAsset.TargetAssetPath} -v:n", + AcceptanceFixture.NuGetGlobalPackagesFolder.Path, + failIfReturnValueIsNotZero: false, + cancellationToken: TestContext.CancellationToken); SL.Build binLog = SL.Serialization.Read(compilationResult.BinlogPath!); - SL.Target generateTestingPlatformEntryPoint = binLog.FindChildrenRecursive().Single(t => t.Name == "_GenerateTestingPlatformEntryPoint"); + + IEnumerable generateTestingPlatformEntryPointTargets = binLog.FindChildrenRecursive().Where(t => t.Name == "_GenerateTestingPlatformEntryPoint"); + + // Working around MSBuild regression: waiting for fix https://github.com/dotnet/msbuild/pull/12431 + // After we insert a new SDK version that ships with a working MSBuild, the IsEmpty assert will fail. + // Then, remove the IsEmpty line, and bring back the code in #if false. + Assert.IsEmpty(generateTestingPlatformEntryPointTargets); +#if false + SL.Target generateTestingPlatformEntryPoint = generateTestingPlatformEntryPointTargets.Single(); Assert.AreEqual("Target \"_GenerateTestingPlatformEntryPoint\" skipped, due to false condition; ( '$(GenerateTestingPlatformEntryPoint)' == 'true' ) was evaluated as ( 'False' == 'true' ).", ((SL.Message)generateTestingPlatformEntryPoint.Children[0]).Text); +#endif + SL.Target includeGenerateTestingPlatformEntryPointIntoCompilation = binLog.FindChildrenRecursive().Single(t => t.Name == "_IncludeGenerateTestingPlatformEntryPointIntoCompilation"); Assert.IsEmpty(includeGenerateTestingPlatformEntryPointIntoCompilation.Children); - Assert.AreNotEqual(0, compilationResult.ExitCode); + compilationResult.AssertExitCodeIsNot(0); } [DynamicData(nameof(GetBuildMatrixTfmBuildVerbConfiguration), typeof(AcceptanceTestBase))] @@ -119,8 +133,8 @@ private async Task GenerateAndVerifyLanguageSpecificEntryPointAsync(string asset .PatchCodeWithReplace("$TargetFrameworks$", tfm) .PatchCodeWithReplace("$MicrosoftTestingPlatformVersion$", MicrosoftTestingPlatformVersion); using TestAsset testAsset = await TestAsset.GenerateAssetAsync(assetName, finalSourceCode); - await DotnetCli.RunAsync($"restore -r {RID} {testAsset.TargetAssetPath}{Path.DirectorySeparatorChar}MSBuildTests.{languageFileExtension}proj", AcceptanceFixture.NuGetGlobalPackagesFolder.Path); - DotnetMuxerResult buildResult = await DotnetCli.RunAsync($"{(verb == Verb.publish ? $"publish -f {tfm}" : "build")} -c {compilationMode} -r {RID} -nodeReuse:false {testAsset.TargetAssetPath} -v:n", AcceptanceFixture.NuGetGlobalPackagesFolder.Path); + await DotnetCli.RunAsync($"restore -r {RID} {testAsset.TargetAssetPath}{Path.DirectorySeparatorChar}MSBuildTests.{languageFileExtension}proj", AcceptanceFixture.NuGetGlobalPackagesFolder.Path, cancellationToken: TestContext.CancellationToken); + DotnetMuxerResult buildResult = await DotnetCli.RunAsync($"{(verb == Verb.publish ? $"publish -f {tfm}" : "build")} -c {compilationMode} -r {RID} -nodeReuse:false {testAsset.TargetAssetPath} -v:n", AcceptanceFixture.NuGetGlobalPackagesFolder.Path, cancellationToken: TestContext.CancellationToken); SL.Build binLog = SL.Serialization.Read(buildResult.BinlogPath!); SL.Target generateTestingPlatformEntryPoint = binLog.FindChildrenRecursive().Single(t => t.Name == "_GenerateTestingPlatformEntryPoint"); SL.Task testingPlatformEntryPoint = generateTestingPlatformEntryPoint.FindChildrenRecursive().Single(t => t.Name == "TestingPlatformEntryPointTask"); @@ -128,9 +142,9 @@ private async Task GenerateAndVerifyLanguageSpecificEntryPointAsync(string asset 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 testHostResult = await testHost.ExecuteAsync(cancellationToken: TestContext.CancellationToken); testHostResult.AssertExitCodeIs(ExitCodes.Success); - Assert.IsTrue(testHostResult.StandardOutput.Contains("Passed!")); + Assert.Contains("Passed!", testHostResult.StandardOutput); SL.Target coreCompile = binLog.FindChildrenRecursive().Single(t => t.Name == "CoreCompile" && t.Children.Count > 0); SL.Task csc = coreCompile.FindChildrenRecursive(t => t.Name == cscProcessName).Single(); @@ -139,15 +153,15 @@ private async Task GenerateAndVerifyLanguageSpecificEntryPointAsync(string asset Assert.IsNotNull(sourceFilePathInObj); File.Delete(buildResult.BinlogPath!); - buildResult = await DotnetCli.RunAsync($"{(verb == Verb.publish ? $"publish -f {tfm}" : "build")} -c {compilationMode} -r {RID} -nodeReuse:false {testAsset.TargetAssetPath} -v:n", AcceptanceFixture.NuGetGlobalPackagesFolder.Path); + buildResult = await DotnetCli.RunAsync($"{(verb == Verb.publish ? $"publish -f {tfm}" : "build")} -c {compilationMode} -r {RID} -nodeReuse:false {testAsset.TargetAssetPath} -v:n", AcceptanceFixture.NuGetGlobalPackagesFolder.Path, cancellationToken: TestContext.CancellationToken); binLog = SL.Serialization.Read(buildResult.BinlogPath!); 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(); + testHostResult = await testHost.ExecuteAsync(cancellationToken: TestContext.CancellationToken); Assert.AreEqual(ExitCodes.Success, testHostResult.ExitCode); - Assert.IsTrue(testHostResult.StandardOutput.Contains("Passed!")); + Assert.Contains("Passed!", testHostResult.StandardOutput); } private const string CSharpSourceCode = """ @@ -453,4 +467,6 @@ let AddExtensions (testApplicationBuilder : ITestApplicationBuilder, args: strin let AddExtensions (_, _) = () """; + + public TestContext TestContext { get; set; } } diff --git a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/MSBuildTests.Solution.cs b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/MSBuildTests.Solution.cs index 834abfb43e..6d000d221b 100644 --- a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/MSBuildTests.Solution.cs +++ b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/MSBuildTests.Solution.cs @@ -42,9 +42,12 @@ public async Task MSBuildTests_UseMSBuildTestInfrastructure_Should_Run_Solution_ string solutionFolder = Path.Combine(tempDirectory.Path, "Solution"); VSSolution solution = new(solutionFolder, "MSTestSolution"); string nugetFile = solution.AddOrUpdateFileContent("Nuget.config", nugetConfigContent); - solution.AddOrUpdateFileContent("dotnet.config", """ - [dotnet.test.runner] - name= "VSTest" + solution.AddOrUpdateFileContent("global.json", """ + { + "test": { + "runner": "VSTest" + } + } """); for (int i = 0; i < 3; i++) { @@ -57,9 +60,9 @@ public async Task MSBuildTests_UseMSBuildTestInfrastructure_Should_Run_Solution_ } // Build the solution - DotnetMuxerResult restoreResult = await DotnetCli.RunAsync($"restore -nodeReuse:false {solution.SolutionFile} --configfile {nugetFile}", AcceptanceFixture.NuGetGlobalPackagesFolder.Path); + DotnetMuxerResult restoreResult = await DotnetCli.RunAsync($"restore -nodeReuse:false {solution.SolutionFile} --configfile {nugetFile}", AcceptanceFixture.NuGetGlobalPackagesFolder.Path, cancellationToken: TestContext.CancellationToken); restoreResult.AssertOutputDoesNotContain("An approximate best match of"); - DotnetMuxerResult testResult = await DotnetCli.RunAsync($"{command} -nodeReuse:false {solution.SolutionFile}", AcceptanceFixture.NuGetGlobalPackagesFolder.Path, workingDirectory: solution.FolderPath); + DotnetMuxerResult testResult = await DotnetCli.RunAsync($"{command} -nodeReuse:false {solution.SolutionFile}", AcceptanceFixture.NuGetGlobalPackagesFolder.Path, workingDirectory: solution.FolderPath, cancellationToken: TestContext.CancellationToken); if (isMultiTfm) { @@ -151,4 +154,6 @@ public async Task ExecuteRequestAsync(ExecuteRequestContext context) } } """; + + public TestContext TestContext { get; set; } } diff --git a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/MSBuildTests.Test.cs b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/MSBuildTests.Test.cs index 18454fe46c..70b3ec3947 100644 --- a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/MSBuildTests.Test.cs +++ b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/MSBuildTests.Test.cs @@ -50,11 +50,11 @@ private async Task InvokeTestingPlatform_Target_Should_Execute_Tests_Without_Sho AssetName, SourceCode .PatchCodeWithReplace("$PlatformTarget$", "x64") - .PatchCodeWithReplace("$TargetFrameworks$", isMultiTfm ? $"{tfm}" : $"{tfm}") + .PatchCodeWithReplace("$TargetFrameworks$", isMultiTfm ? $"{tfm}" : $"{tfm}") .PatchCodeWithReplace("$AssertValue$", testSucceeded.ToString().ToLowerInvariant()) .PatchCodeWithReplace("$MicrosoftTestingPlatformVersion$", MicrosoftTestingPlatformVersion)); 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 \"{testAsset.TargetAssetPath}\"", AcceptanceFixture.NuGetGlobalPackagesFolder.Path, workingDirectory: testAsset.TargetAssetPath, failIfReturnValueIsNotZero: false); + DotnetMuxerResult compilationResult = await DotnetCli.RunAsync($"{testCommand} -p:TestingPlatformCommandLineArguments=\"--results-directory %22{testResultFolder}%22\" -p:Configuration={compilationMode} -p:nodeReuse=false \"{testAsset.TargetAssetPath}\"", AcceptanceFixture.NuGetGlobalPackagesFolder.Path, workingDirectory: testAsset.TargetAssetPath, failIfReturnValueIsNotZero: false, cancellationToken: TestContext.CancellationToken); foreach (string tfmToAssert in tfmsToAssert) { @@ -78,7 +78,7 @@ private async Task InvokeTestingPlatform_Target_Should_Build_Without_Warnings_An AssetName, SourceCode .PatchCodeWithReplace("$PlatformTarget$", "x64") - .PatchCodeWithReplace("$TargetFrameworks$", isMultiTfm ? $"{tfm}" : $"{tfm}") + .PatchCodeWithReplace("$TargetFrameworks$", isMultiTfm ? $"{tfm}" : $"{tfm}") .PatchCodeWithReplace("$AssertValue$", testSucceeded.ToString().ToLowerInvariant()) .PatchCodeWithReplace("$MicrosoftTestingPlatformVersion$", MicrosoftTestingPlatformVersion)); string testResultFolder = Path.Combine(testAsset.TargetAssetPath, Guid.NewGuid().ToString("N")); @@ -87,11 +87,11 @@ private async Task InvokeTestingPlatform_Target_Should_Build_Without_Warnings_An ? await DotnetCli.RunAsync( $"{testCommand} -p:Configuration={compilationMode} -p:nodeReuse=false \"{testAsset.TargetAssetPath}\" -- --treenode-filter --results-directory \"{testResultFolder}\"", AcceptanceFixture.NuGetGlobalPackagesFolder.Path, - workingDirectory: testAsset.TargetAssetPath) + workingDirectory: testAsset.TargetAssetPath, cancellationToken: TestContext.CancellationToken) : await DotnetCli.RunAsync( $"{testCommand} -p:TestingPlatformCommandLineArguments=\"--treenode-filter --results-directory \"{testResultFolder}\"\" -p:Configuration={compilationMode} -p:nodeReuse=false \"{testAsset.TargetAssetPath}\"", AcceptanceFixture.NuGetGlobalPackagesFolder.Path, - workingDirectory: testAsset.TargetAssetPath); + workingDirectory: testAsset.TargetAssetPath, cancellationToken: TestContext.CancellationToken); foreach (string tfmToAssert in tfmsToAssert) { @@ -107,19 +107,22 @@ public async Task RunUsingTestTargetWithNetfxMSBuild() AssetName, SourceCode .PatchCodeWithReplace("$PlatformTarget$", string.Empty) - .PatchCodeWithReplace("$TargetFrameworks$", $"{TargetFrameworks.NetCurrent}") + .PatchCodeWithReplace("$TargetFrameworks$", $"{TargetFrameworks.NetCurrent}") .PatchCodeWithReplace("$AssertValue$", bool.TrueString.ToLowerInvariant()) .PatchCodeWithReplace("$MicrosoftTestingPlatformVersion$", MicrosoftTestingPlatformVersion)); - string msbuildExe = await FindMsbuildWithVsWhereAsync(); + string msbuildExe = await FindMsbuildWithVsWhereAsync(TestContext.CancellationToken); var commandLine = new TestInfrastructure.CommandLine(); string binlogFile = Path.Combine(TempDirectory.TestSuiteDirectory, $"{nameof(RunUsingTestTargetWithNetfxMSBuild)}.binlog"); - await commandLine.RunAsync($"\"{msbuildExe}\" {testAsset.TargetAssetPath} /t:Restore"); - await commandLine.RunAsync($"\"{msbuildExe}\" {testAsset.TargetAssetPath} /t:\"Build;Test\" /bl:\"{binlogFile}\"", environmentVariables: new Dictionary - { - ["DOTNET_ROOT"] = Path.Combine(RootFinder.Find(), ".dotnet"), - }); - StringAssert.Contains(commandLine.StandardOutput, "Tests succeeded"); + await commandLine.RunAsync($"\"{msbuildExe}\" {testAsset.TargetAssetPath} /t:Restore", cancellationToken: TestContext.CancellationToken); + await commandLine.RunAsync( + $"\"{msbuildExe}\" {testAsset.TargetAssetPath} /t:\"Build;Test\" /bl:\"{binlogFile}\"", + environmentVariables: new Dictionary + { + ["DOTNET_ROOT"] = Path.Combine(RootFinder.Find(), ".dotnet"), + }, + cancellationToken: TestContext.CancellationToken); + Assert.Contains("Tests succeeded", commandLine.StandardOutput); } [TestMethod] @@ -149,7 +152,8 @@ await DotnetCli.RunAsync( AcceptanceFixture.NuGetGlobalPackagesFolder.Path, workingDirectory: testAsset.TargetAssetPath, environmentVariables: dotnetRootX86, - failIfReturnValueIsNotZero: false); + failIfReturnValueIsNotZero: false, + cancellationToken: TestContext.CancellationToken); string outputFileLog = Directory.GetFiles(testAsset.TargetAssetPath, "MSBuild Tests_net9.0_x86.log", SearchOption.AllDirectories).Single(); Assert.IsTrue(File.Exists(outputFileLog), $"Expected file '{outputFileLog}'"); @@ -175,14 +179,15 @@ public async Task Invoke_DotnetTest_With_Incompatible_Arch() AssetName, SourceCode .PatchCodeWithReplace("$PlatformTarget$", string.Empty) - .PatchCodeWithReplace("$TargetFrameworks$", $"{TargetFrameworks.NetCurrent}") + .PatchCodeWithReplace("$TargetFrameworks$", $"{TargetFrameworks.NetCurrent}") .PatchCodeWithReplace("$AssertValue$", bool.TrueString.ToLowerInvariant()) .PatchCodeWithReplace("$MicrosoftTestingPlatformVersion$", MicrosoftTestingPlatformVersion)); DotnetMuxerResult result = await DotnetCli.RunAsync( $"test --arch {incompatibleArchitecture} -p:TestingPlatformDotnetTestSupport=True \"{testAsset.TargetAssetPath}\"", AcceptanceFixture.NuGetGlobalPackagesFolder.Path, workingDirectory: testAsset.TargetAssetPath, - failIfReturnValueIsNotZero: false); + failIfReturnValueIsNotZero: false, + cancellationToken: TestContext.CancellationToken); // On Windows, we run the exe directly. // On other OSes, we run with dotnet exec. @@ -238,7 +243,8 @@ await DotnetCli.RunAsync( AcceptanceFixture.NuGetGlobalPackagesFolder.Path, workingDirectory: testAsset.TargetAssetPath, environmentVariables: dotnetHostPathEnvVar, - failIfReturnValueIsNotZero: false); + failIfReturnValueIsNotZero: false, + cancellationToken: TestContext.CancellationToken); string outputFileLog = Directory.GetFiles(testAsset.TargetAssetPath, "MSBuild Tests_net9.0_x64.log", SearchOption.AllDirectories).Single(); Assert.IsTrue(File.Exists(outputFileLog), $"Expected file '{outputFileLog}'"); @@ -247,6 +253,51 @@ await DotnetCli.RunAsync( Assert.Contains($"[win-x64 - {TargetFrameworks.NetCurrent}]", logFileContent); } + [TestMethod] + public async Task VSTestProperties_Should_Produce_Warning_When_Set() + { + using TestAsset testAsset = await TestAsset.GenerateAssetAsync( + "VSTestPropertiesValidation", + SourceCode + .PatchCodeWithReplace("$PlatformTarget$", string.Empty) + .PatchCodeWithReplace("$TargetFrameworks$", $"{TargetFrameworks.NetCurrent}") + .PatchCodeWithReplace("$AssertValue$", bool.TrueString.ToLowerInvariant()) + .PatchCodeWithReplace("$MicrosoftTestingPlatformVersion$", MicrosoftTestingPlatformVersion)); + + DotnetMuxerResult result = await DotnetCli.RunAsync( + $"test -p:TestingPlatformDotnetTestSupport=True --filter Category=Unit --logger trx \"{testAsset.TargetAssetPath}\"", + AcceptanceFixture.NuGetGlobalPackagesFolder.Path, + workingDirectory: testAsset.TargetAssetPath, + failIfReturnValueIsNotZero: false, + cancellationToken: TestContext.CancellationToken); + + Assert.AreEqual(1, result.ExitCode); + result.AssertOutputContains("VSTest-specific properties are set but will be ignored when using Microsoft.Testing.Platform."); + result.AssertOutputContains("VSTestTestCaseFilter"); + result.AssertOutputContains("VSTestLogger"); + } + + [TestMethod] + public async Task VSTestProperties_Should_Not_Cause_Error_When_Not_Set() + { + using TestAsset testAsset = await TestAsset.GenerateAssetAsync( + "VSTestPropertiesValidationNoProps", + SourceCode + .PatchCodeWithReplace("$PlatformTarget$", string.Empty) + .PatchCodeWithReplace("$TargetFrameworks$", $"{TargetFrameworks.NetCurrent}") + .PatchCodeWithReplace("$AssertValue$", bool.TrueString.ToLowerInvariant()) + .PatchCodeWithReplace("$MicrosoftTestingPlatformVersion$", MicrosoftTestingPlatformVersion)); + + DotnetMuxerResult result = await DotnetCli.RunAsync( + $"test -p:TestingPlatformDotnetTestSupport=True \"{testAsset.TargetAssetPath}\"", + AcceptanceFixture.NuGetGlobalPackagesFolder.Path, + workingDirectory: testAsset.TargetAssetPath, + failIfReturnValueIsNotZero: false, + cancellationToken: TestContext.CancellationToken); + + Assert.AreEqual(0, result.ExitCode); + } + private static void CommonAssert(DotnetMuxerResult compilationResult, string tfm, bool testSucceeded, string testResultFolder) { Assert.IsTrue(Regex.IsMatch(compilationResult.StandardOutput, $".*Run tests:.* \\[{tfm}|x64\\]"), compilationResult.StandardOutput); @@ -281,10 +332,10 @@ public async Task InvokeTestingPlatform_Target_Showing_Error_And_Do_Not_Capture_ AssetName, SourceCode .PatchCodeWithReplace("$PlatformTarget$", "x64") - .PatchCodeWithReplace("$TargetFrameworks$", $"{tfm}") + .PatchCodeWithReplace("$TargetFrameworks$", $"{tfm}") .PatchCodeWithReplace("$AssertValue$", testSucceeded.ToString().ToLowerInvariant()) .PatchCodeWithReplace("$MicrosoftTestingPlatformVersion$", MicrosoftTestingPlatformVersion)); - DotnetMuxerResult compilationResult = await DotnetCli.RunAsync($"{testCommand} -p:TestingPlatformShowTestsFailure=True -p:TestingPlatformCaptureOutput=False -p:Configuration={compilationMode} -p:nodeReuse=false {testAsset.TargetAssetPath}", AcceptanceFixture.NuGetGlobalPackagesFolder.Path, workingDirectory: testAsset.TargetAssetPath, failIfReturnValueIsNotZero: false); + DotnetMuxerResult compilationResult = await DotnetCli.RunAsync($"{testCommand} -p:TestingPlatformShowTestsFailure=True -p:TestingPlatformCaptureOutput=False -p:Configuration={compilationMode} -p:nodeReuse=false {testAsset.TargetAssetPath}", AcceptanceFixture.NuGetGlobalPackagesFolder.Path, workingDirectory: testAsset.TargetAssetPath, failIfReturnValueIsNotZero: false, cancellationToken: TestContext.CancellationToken); compilationResult.AssertOutputContains("error test failed: Test2 ("); compilationResult.AssertOutputContains("FAILED: Expected 'true', but got 'false'."); @@ -298,10 +349,10 @@ public async Task TestingPlatformDisableCustomTestTarget_Should_Cause_UserDefine AssetName, SourceCode .PatchCodeWithReplace("$PlatformTarget$", "x64") - .PatchCodeWithReplace("$TargetFrameworks$", $"{TargetFrameworks.NetCurrent}") + .PatchCodeWithReplace("$TargetFrameworks$", $"{TargetFrameworks.NetCurrent}") .PatchCodeWithReplace("$AssertValue$", "true") .PatchCodeWithReplace("$MicrosoftTestingPlatformVersion$", MicrosoftTestingPlatformVersion)); - DotnetMuxerResult compilationResult = await DotnetCli.RunAsync($"build {testAsset.TargetAssetPath} -p:TestingPlatformDisableCustomTestTarget=true -p:ImportUserDefinedTestTarget=true -t:\"Build;Test\"", AcceptanceFixture.NuGetGlobalPackagesFolder.Path, failIfReturnValueIsNotZero: false); + DotnetMuxerResult compilationResult = await DotnetCli.RunAsync($"build {testAsset.TargetAssetPath} -p:TestingPlatformDisableCustomTestTarget=true -p:ImportUserDefinedTestTarget=true -t:\"Build;Test\"", AcceptanceFixture.NuGetGlobalPackagesFolder.Path, failIfReturnValueIsNotZero: false, cancellationToken: TestContext.CancellationToken); compilationResult.AssertOutputContains("Error from UserDefinedTestTarget.targets"); } @@ -335,9 +386,12 @@ public async Task TestingPlatformDisableCustomTestTarget_Should_Cause_UserDefine -#file dotnet.config -[dotnet.test.runner] -name= "VSTest" +#file global.json +{ + "test": { + "runner": "VSTest" + } +} #file Program.cs using Microsoft.Testing.Platform.Builder; @@ -424,4 +478,6 @@ public async Task ExecuteRequestAsync(ExecuteRequestContext context) } } """; + + public TestContext TestContext { get; set; } } diff --git a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/MaxFailedTestsExtensionTests.cs b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/MaxFailedTestsExtensionTests.cs index 44ebf81d34..528de217d2 100644 --- a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/MaxFailedTestsExtensionTests.cs +++ b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/MaxFailedTestsExtensionTests.cs @@ -12,7 +12,7 @@ public class MaxFailedTestsExtensionTests : AcceptanceTestBase public async Task UsingNoBanner_TheBannerDoesNotAppear(string tfm) { var testHost = TestInfrastructure.TestHost.LocateFrom(AssetFixture.TargetAssetPath, AssetName, tfm); - TestHostResult testHostResult = await testHost.ExecuteAsync("--no-banner"); + TestHostResult testHostResult = await testHost.ExecuteAsync("--no-banner", cancellationToken: TestContext.CancellationToken); testHostResult.AssertExitCodeIs(ExitCodes.ZeroTests); testHostResult.AssertOutputDoesNotMatchRegex(_bannerRegexMatchPattern); @@ -30,7 +30,8 @@ public async Task UsingNoBanner_InTheEnvironmentVars_TheBannerDoesNotAppear(stri new Dictionary { { "TESTINGPLATFORM_NOBANNER", "true" }, - }); + }, + cancellationToken: TestContext.CancellationToken); testHostResult.AssertExitCodeIs(ExitCodes.ZeroTests); testHostResult.AssertOutputDoesNotMatchRegex(_bannerRegexMatchPattern); @@ -46,7 +47,8 @@ public async Task UsingDotnetNoLogo_InTheEnvironmentVars_TheBannerDoesNotAppear( new Dictionary { { "DOTNET_NOLOGO", "true" }, - }); + }, + cancellationToken: TestContext.CancellationToken); testHostResult.AssertExitCodeIs(ExitCodes.ZeroTests); testHostResult.AssertOutputDoesNotMatchRegex(_bannerRegexMatchPattern); @@ -57,7 +59,7 @@ public async Task UsingDotnetNoLogo_InTheEnvironmentVars_TheBannerDoesNotAppear( public async Task WithoutUsingNoBanner_TheBannerAppears(string tfm) { var testHost = TestInfrastructure.TestHost.LocateFrom(AssetFixture.TargetAssetPath, AssetName, tfm); - TestHostResult testHostResult = await testHost.ExecuteAsync(); + TestHostResult testHostResult = await testHost.ExecuteAsync(cancellationToken: TestContext.CancellationToken); testHostResult.AssertExitCodeIs(ExitCodes.ZeroTests); testHostResult.AssertOutputMatchesRegex(_bannerRegexMatchPattern); @@ -132,4 +134,6 @@ public Task ExecuteRequestAsync(ExecuteRequestContext context) .PatchCodeWithReplace("$MicrosoftTestingPlatformVersion$", MicrosoftTestingPlatformVersion)); } } + + public TestContext TestContext { get; set; } } diff --git a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/RetryFailedTestsTests.cs b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/RetryFailedTestsTests.cs index d971231e9b..f29b77ee6f 100644 --- a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/RetryFailedTestsTests.cs +++ b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/RetryFailedTestsTests.cs @@ -28,14 +28,15 @@ public async Task RetryFailedTests_OnlyRetryTimes_Succeeds(string tfm, bool fail var testHost = TestInfrastructure.TestHost.LocateFrom(AssetFixture.TargetAssetPath, AssetName, tfm); string resultDirectory = Path.Combine(testHost.DirectoryName, Guid.NewGuid().ToString("N")); TestHostResult testHostResult = await testHost.ExecuteAsync( - $"--retry-failed-tests 3 --results-directory {resultDirectory}", + $"--retry-failed-tests 3 --results-directory {resultDirectory} --report-trx", new() { { EnvironmentVariableConstants.TESTINGPLATFORM_TELEMETRY_OPTOUT, "1" }, { "METHOD1", "1" }, { "FAIL", failOnly ? "1" : "0" }, { "RESULTDIR", resultDirectory }, - }); + }, + cancellationToken: TestContext.CancellationToken); if (!failOnly) { @@ -43,6 +44,15 @@ public async Task RetryFailedTests_OnlyRetryTimes_Succeeds(string tfm, bool fail testHostResult.AssertOutputContains("Tests suite completed successfully in 2 attempts"); testHostResult.AssertOutputContains("Failed! -"); testHostResult.AssertOutputContains("Passed! -"); + + string[] trxFiles = Directory.GetFiles(resultDirectory, "*.trx", SearchOption.AllDirectories); + Assert.HasCount(2, trxFiles); + string trxContents1 = File.ReadAllText(trxFiles[0]); + string trxContents2 = File.ReadAllText(trxFiles[1]); + Assert.AreNotEqual(trxContents1, trxContents2); + string id1 = Regex.Match(trxContents1, " !x.Contains("Retries", StringComparison.OrdinalIgnoreCase)) - .ToArray(); + string[] entries = [.. Directory.GetFiles(resultDirectory, "*.*", SearchOption.AllDirectories).Where(x => !x.Contains("Retries", StringComparison.OrdinalIgnoreCase))]; // 1 trx file Assert.AreEqual(1, entries.Count(x => x.EndsWith("trx", StringComparison.OrdinalIgnoreCase))); @@ -180,6 +190,31 @@ await RetryHelper.RetryAsync( }, 3, TimeSpan.FromSeconds(5)); } + [TestMethod] + public async Task RetryFailedTests_PassingFromFirstTime_UsingOldDotnetTest_MoveFiles_Succeeds() + { + string resultDirectory = Path.Combine(AssetFixture.TargetAssetPath, Guid.NewGuid().ToString("N")); + + DotnetMuxerResult result = await DotnetCli.RunAsync( + $"test \"{AssetFixture.TargetAssetPath}\" -- --retry-failed-tests 1 --results-directory \"{resultDirectory}\"", + AcceptanceFixture.NuGetGlobalPackagesFolder.Path, + workingDirectory: AssetFixture.TargetAssetPath, cancellationToken: TestContext.CancellationToken); + + result.AssertExitCodeIs(ExitCodes.Success); + + // File names are on the form: RetryFailedTests_tfm_architecture.log + string[] logFilesFromInvokeTestingPlatformTask = Directory.GetFiles(resultDirectory, "RetryFailedTests_*_*.log"); + Assert.HasCount(TargetFrameworks.All.Length, logFilesFromInvokeTestingPlatformTask); + foreach (string logFile in logFilesFromInvokeTestingPlatformTask) + { + string logFileContents = File.ReadAllText(logFile); + Assert.Contains("Test run summary: Passed!", logFileContents); + Assert.Contains("total: 3", logFileContents); + Assert.Contains("succeeded: 3", logFileContents); + Assert.Contains("Tests suite completed successfully in 1 attempts", logFileContents); + } + } + public sealed class TestAssetFixture() : TestAssetFixtureBase(AcceptanceFixture.NuGetGlobalPackagesFolder) { public string TargetAssetPath => GetAssetPath(AssetName); @@ -202,14 +237,25 @@ public sealed class TestAssetFixture() : TestAssetFixtureBase(AcceptanceFixture. Exe true preview + false + true + false + +#file global.json +{ + "test": { + "runner": "VSTest" + } +} + #file Program.cs using Microsoft.Testing.Extensions; using Microsoft.Testing.Extensions.TrxReport.Abstractions; @@ -217,6 +263,7 @@ public sealed class TestAssetFixture() : TestAssetFixtureBase(AcceptanceFixture. using Microsoft.Testing.Platform.Capabilities.TestFramework; using Microsoft.Testing.Platform.Extensions.Messages; using Microsoft.Testing.Platform.Extensions.TestFramework; +using Microsoft.Testing.Platform.MSBuild; using Microsoft.Testing.Platform.Services; public class Program @@ -230,6 +277,7 @@ public static async Task Main(string[] args) builder.AddCrashDumpProvider(); builder.AddTrxReportProvider(); builder.AddRetryProvider(); + builder.AddMSBuild(); using ITestApplication app = await builder.BuildAsync(); return await app.RunAsync(); } @@ -270,7 +318,7 @@ public async Task ExecuteRequestAsync(ExecuteRequestContext context) string resultDir = Environment.GetEnvironmentVariable("RESULTDIR")!; bool crash = Environment.GetEnvironmentVariable("CRASH") == "1"; - if (await TestMethod1(fail, resultDir, crash)) + if (TestMethod1(fail, resultDir, crash)) { await context.MessageBus.PublishAsync(this, new TestNodeUpdateMessage(context.Request.Session.SessionUid, new TestNode() { Uid = "1", DisplayName = "TestMethod1", Properties = new(PassedTestNodeStateProperty.CachedInstance) })); @@ -281,7 +329,7 @@ public async Task ExecuteRequestAsync(ExecuteRequestContext context) new TestNode() { Uid = "1", DisplayName = "TestMethod1", Properties = new(new FailedTestNodeStateProperty()) })); } - if (await TestMethod2(fail, resultDir)) + if (TestMethod2(fail, resultDir)) { await context.MessageBus.PublishAsync(this, new TestNodeUpdateMessage(context.Request.Session.SessionUid, new TestNode() { Uid = "2", DisplayName = "TestMethod2", Properties = new(PassedTestNodeStateProperty.CachedInstance) })); @@ -292,7 +340,7 @@ public async Task ExecuteRequestAsync(ExecuteRequestContext context) new TestNode() { Uid = "2", DisplayName = "TestMethod2", Properties = new(new FailedTestNodeStateProperty()) })); } - if (await TestMethod3(fail, resultDir)) + if (TestMethod3(fail, resultDir)) { await context.MessageBus.PublishAsync(this, new TestNodeUpdateMessage(context.Request.Session.SessionUid, new TestNode() { Uid = "3", DisplayName = "TestMethod3", Properties = new(PassedTestNodeStateProperty.CachedInstance) })); @@ -306,7 +354,7 @@ public async Task ExecuteRequestAsync(ExecuteRequestContext context) context.Complete(); } - private async Task TestMethod1(bool fail, string resultDir, bool crash) + private bool TestMethod1(bool fail, string resultDir, bool crash) { if (crash) { @@ -330,7 +378,7 @@ private async Task TestMethod1(bool fail, string resultDir, bool crash) return assert; } - private async Task TestMethod2(bool fail, string resultDir) + private bool TestMethod2(bool fail, string resultDir) { bool envVar = Environment.GetEnvironmentVariable("METHOD2") is null; System.Console.WriteLine("envVar " + envVar); @@ -350,7 +398,7 @@ private async Task TestMethod2(bool fail, string resultDir) return assert; } - private async Task TestMethod3(bool fail, string resultDir) + private bool TestMethod3(bool fail, string resultDir) { bool envVar = Environment.GetEnvironmentVariable("METHOD3") is null; @@ -371,4 +419,6 @@ private async Task TestMethod3(bool fail, string resultDir) } """; } + + public TestContext TestContext { get; set; } } diff --git a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/ServerLoggingTests.cs b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/ServerLoggingTests.cs index 3bfd65e5ea..8b161bc701 100644 --- a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/ServerLoggingTests.cs +++ b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/ServerLoggingTests.cs @@ -17,7 +17,7 @@ public async Task RunningInServerJsonRpcModeShouldHaveOutputDeviceLogsPushedToTe string resultDirectory = Path.Combine(AssetFixture.TargetAssetPath, Guid.NewGuid().ToString("N"), tfm); var testHost = TestInfrastructure.TestHost.LocateFrom(AssetFixture.TargetAssetPath, "ServerLoggingTests", tfm); using TestingPlatformClient jsonClient = await StartAsServerAndConnectToTheClientAsync(testHost); - LogsCollector logs = new(); + LogsCollector logs = []; jsonClient.RegisterLogListener(logs); InitializeResponse initializeResponseArgs = await jsonClient.Initialize(); @@ -29,7 +29,7 @@ public async Task RunningInServerJsonRpcModeShouldHaveOutputDeviceLogsPushedToTe ResponseListener runListener = await jsonClient.RunTests(Guid.NewGuid(), runCollector.CollectNodeUpdates); await Task.WhenAll(discoveryListener.WaitCompletion(), runListener.WaitCompletion()); - Assert.IsFalse(logs.Count == 0, "Logs are empty"); + Assert.AreNotEqual(0, logs.Count, "Logs are empty"); string logsString = string.Join(Environment.NewLine, logs.Select(l => l.ToString())); string logPath = LogFilePathRegex().Match(logsString).Groups[1].Value; string port = PortRegex().Match(logsString).Groups[1].Value; diff --git a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/ServerMode/TestNodeUpdateCollector.cs b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/ServerMode/TestNodeUpdateCollector.cs index 4df559bc05..18efef8eae 100644 --- a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/ServerMode/TestNodeUpdateCollector.cs +++ b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/ServerMode/TestNodeUpdateCollector.cs @@ -10,7 +10,7 @@ public class TestNodeUpdateCollector private readonly TaskCompletionSource _taskCompletionSource = new(); private readonly Func? _completeCollector; - public ConcurrentBag TestNodeUpdates { get; } = new(); + public ConcurrentBag TestNodeUpdates { get; } = []; public TestNodeUpdateCollector(Func? completeCollectorWhen = null) => _completeCollector = completeCollectorWhen; diff --git a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/ServerMode/v1.0.0/TestingPlatformClient.cs b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/ServerMode/v1.0.0/TestingPlatformClient.cs index 6dda46477f..16504cd25d 100644 --- a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/ServerMode/v1.0.0/TestingPlatformClient.cs +++ b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/ServerMode/v1.0.0/TestingPlatformClient.cs @@ -50,9 +50,9 @@ private void JsonRpcClient_Disconnected(object? sender, JsonRpcDisconnectedEvent public int ExitCode => _processHandler.ExitCode; - public async Task WaitServerProcessExit() + public async Task WaitServerProcessExit(CancellationToken cancellationToken) { - await _processHandler.WaitForExitAsync(); + await _processHandler.WaitForExitAsync(cancellationToken); return _processHandler.ExitCode; } @@ -64,14 +64,9 @@ private async Task CheckedInvoke(Func func) { await func(); } - catch (Exception ex) + catch (Exception ex) when (_disconnectionReason.Length > 0) { - if (_disconnectionReason.Length > 0) - { - throw new InvalidOperationException($"{ex.Message}\n{_disconnectionReason}", ex); - } - - throw; + throw new InvalidOperationException($"{ex.Message}\n{_disconnectionReason}", ex); } } @@ -157,14 +152,11 @@ public record Log(LogLevel LogLevel, string Message); private sealed class TargetHandler { - private readonly ConcurrentDictionary _listeners - = new(); + private readonly ConcurrentDictionary _listeners = new(); - private readonly ConcurrentBag _logListeners - = new(); + private readonly ConcurrentBag _logListeners = []; - private readonly ConcurrentBag _telemetryPayloads - = new(); + private readonly ConcurrentBag _telemetryPayloads = []; public void RegisterTelemetryListener(TelemetryCollector listener) => _telemetryPayloads.Add(listener); diff --git a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/TelemetryTests.cs b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/TelemetryTests.cs index 53abc787a6..c3916ceab6 100644 --- a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/TelemetryTests.cs +++ b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/TelemetryTests.cs @@ -18,7 +18,7 @@ public async Task Telemetry_ByDefault_TelemetryIsEnabled(string tfm) string diagPathPattern = Path.Combine(diagPath, @"log_.*.diag").Replace(@"\", @"\\"); var testHost = TestInfrastructure.TestHost.LocateFrom(AssetFixture.TargetAssetPath, AssetName, tfm); - TestHostResult testHostResult = await testHost.ExecuteAsync("--diagnostic", disableTelemetry: false); + TestHostResult testHostResult = await testHost.ExecuteAsync("--diagnostic", disableTelemetry: false, cancellationToken: TestContext.CancellationToken); testHostResult.AssertExitCodeIs(ExitCodes.ZeroTests); @@ -46,7 +46,7 @@ public async Task Telemetry_WhenOptingOutTelemetry_WithEnvironmentVariable_Telem { { EnvironmentVariableConstants.TESTINGPLATFORM_TELEMETRY_OPTOUT, "1" }, }, - disableTelemetry: false); + disableTelemetry: false, TestContext.CancellationToken); testHostResult.AssertExitCodeIs(ExitCodes.ZeroTests); @@ -74,7 +74,7 @@ public async Task Telemetry_WhenOptingOutTelemetry_With_DOTNET_CLI_EnvironmentVa { { EnvironmentVariableConstants.DOTNET_CLI_TELEMETRY_OPTOUT, "1" }, }, - disableTelemetry: false); + disableTelemetry: false, TestContext.CancellationToken); testHostResult.AssertExitCodeIs(ExitCodes.ZeroTests); @@ -96,7 +96,7 @@ public async Task Telemetry_WhenEnableTelemetryIsFalse_WithTestApplicationOption string diagPathPattern = Path.Combine(diagPath, @"log_.*.diag").Replace(@"\", @"\\"); var testHost = TestInfrastructure.TestHost.LocateFrom(AssetFixture.TargetAssetPathWithDisableTelemetry, AssetName, tfm); - TestHostResult testHostResult = await testHost.ExecuteAsync("--diagnostic", disableTelemetry: false); + TestHostResult testHostResult = await testHost.ExecuteAsync("--diagnostic", disableTelemetry: false, cancellationToken: TestContext.CancellationToken); testHostResult.AssertExitCodeIs(ExitCodes.ZeroTests); @@ -212,4 +212,6 @@ public Task ExecuteRequestAsync(ExecuteRequestContext context) .PatchCodeWithReplace("$TelemetryArg$", ", new TestApplicationOptions() { EnableTelemetry = false }")); } } + + public TestContext TestContext { get; set; } } diff --git a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/TestHostProcessLifetimeHandlerTests.cs b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/TestHostProcessLifetimeHandlerTests.cs index cd3452d3df..c0327972fa 100644 --- a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/TestHostProcessLifetimeHandlerTests.cs +++ b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/TestHostProcessLifetimeHandlerTests.cs @@ -13,7 +13,7 @@ public sealed class TestHostProcessLifetimeHandlerTests : AcceptanceTestBase public async Task TimeoutWithInvalidArg_WithoutLetterSuffix_OutputInvalidMessage(string tfm) { var testHost = TestInfrastructure.TestHost.LocateFrom(AssetFixture.NoExtensionTargetAssetPath, TestAssetFixture.AssetName, tfm); - TestHostResult testHostResult = await testHost.ExecuteAsync("--timeout 5"); + TestHostResult testHostResult = await testHost.ExecuteAsync("--timeout 5", cancellationToken: TestContext.CancellationToken); testHostResult.AssertExitCodeIs(ExitCodes.InvalidCommandLine); - testHostResult.StandardError.Contains("'timeout' option should have one argument as string in the format [h|m|s] where 'value' is float"); + testHostResult.AssertOutputContains("'timeout' option should have one argument as string in the format [h|m|s] where 'value' is float"); } [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] @@ -22,10 +22,10 @@ public async Task TimeoutWithInvalidArg_WithoutLetterSuffix_OutputInvalidMessage public async Task TimeoutWithInvalidArg_WithInvalidLetterSuffix_OutputInvalidMessage(string tfm) { var testHost = TestInfrastructure.TestHost.LocateFrom(AssetFixture.NoExtensionTargetAssetPath, TestAssetFixture.AssetName, tfm); - TestHostResult testHostResult = await testHost.ExecuteAsync("--timeout 5y"); + TestHostResult testHostResult = await testHost.ExecuteAsync("--timeout 5y", cancellationToken: TestContext.CancellationToken); testHostResult.AssertExitCodeIs(ExitCodes.InvalidCommandLine); - testHostResult.StandardError.Contains("'timeout' option should have one argument as string in the format [h|m|s] where 'value' is float"); + testHostResult.AssertOutputContains("'timeout' option should have one argument as string in the format [h|m|s] where 'value' is float"); } [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] @@ -33,10 +33,10 @@ public async Task TimeoutWithInvalidArg_WithInvalidLetterSuffix_OutputInvalidMes public async Task TimeoutWithInvalidArg_WithInvalidFormat_OutputInvalidMessage(string tfm) { var testHost = TestInfrastructure.TestHost.LocateFrom(AssetFixture.NoExtensionTargetAssetPath, TestAssetFixture.AssetName, tfm); - TestHostResult testHostResult = await testHost.ExecuteAsync("--timeout 5h6m"); + TestHostResult testHostResult = await testHost.ExecuteAsync("--timeout 5h6m", cancellationToken: TestContext.CancellationToken); testHostResult.AssertExitCodeIs(ExitCodes.InvalidCommandLine); - testHostResult.StandardError.Contains("'timeout' option should have one argument as string in the format [h|m|s] where 'value' is float"); + testHostResult.AssertOutputContains("'timeout' option should have one argument as string in the format [h|m|s] where 'value' is float"); } [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] @@ -44,10 +44,10 @@ public async Task TimeoutWithInvalidArg_WithInvalidFormat_OutputInvalidMessage(s public async Task TimeoutWithValidArg_WithTestTimeOut_OutputContainsCancelingMessage(string tfm) { var testHost = TestInfrastructure.TestHost.LocateFrom(AssetFixture.NoExtensionTargetAssetPath, TestAssetFixture.AssetName, tfm); - TestHostResult testHostResult = await testHost.ExecuteAsync("--timeout 1s"); + TestHostResult testHostResult = await testHost.ExecuteAsync("--timeout 1s", cancellationToken: TestContext.CancellationToken); testHostResult.AssertExitCodeIsNot(ExitCodes.Success); - testHostResult.StandardOutput.Contains("Canceling the test session"); + testHostResult.AssertOutputContains("Canceling the test session"); } [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] @@ -55,12 +55,10 @@ public async Task TimeoutWithValidArg_WithTestTimeOut_OutputContainsCancelingMes public async Task TimeoutWithValidArg_WithSecondAsSuffix_WithTestNotTimeOut_OutputDoesNotContainCancelingMessage(string tfm) { var testHost = TestInfrastructure.TestHost.LocateFrom(AssetFixture.NoExtensionTargetAssetPath, TestAssetFixture.AssetName, tfm); - TestHostResult testHostResult = await testHost.ExecuteAsync("--timeout 12.5s"); + TestHostResult testHostResult = await testHost.ExecuteAsync("--timeout 12.5s", cancellationToken: TestContext.CancellationToken); testHostResult.AssertExitCodeIs(ExitCodes.ZeroTests); - - string output = testHostResult.StandardOutput; - Assert.IsFalse(output.Contains("Canceling the test session")); + testHostResult.AssertOutputDoesNotContain("Canceling the test session"); } [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] @@ -68,12 +66,10 @@ public async Task TimeoutWithValidArg_WithSecondAsSuffix_WithTestNotTimeOut_Outp public async Task TimeoutWithValidArg_WithMinuteAsSuffix_WithTestNotTimeOut_OutputDoesNotContainCancelingMessage(string tfm) { var testHost = TestInfrastructure.TestHost.LocateFrom(AssetFixture.NoExtensionTargetAssetPath, TestAssetFixture.AssetName, tfm); - TestHostResult testHostResult = await testHost.ExecuteAsync("--timeout 1m"); + TestHostResult testHostResult = await testHost.ExecuteAsync("--timeout 1m", cancellationToken: TestContext.CancellationToken); testHostResult.AssertExitCodeIs(ExitCodes.ZeroTests); - - string output = testHostResult.StandardOutput; - Assert.IsFalse(output.Contains("Canceling the test session")); + testHostResult.AssertOutputDoesNotContain("Canceling the test session"); } [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] @@ -81,12 +77,10 @@ public async Task TimeoutWithValidArg_WithMinuteAsSuffix_WithTestNotTimeOut_Outp public async Task TimeoutWithValidArg_WithHourAsSuffix_WithTestNotTimeOut_OutputDoesNotContainCancelingMessage(string tfm) { var testHost = TestInfrastructure.TestHost.LocateFrom(AssetFixture.NoExtensionTargetAssetPath, TestAssetFixture.AssetName, tfm); - TestHostResult testHostResult = await testHost.ExecuteAsync("--timeout 1h"); + TestHostResult testHostResult = await testHost.ExecuteAsync("--timeout 1h", cancellationToken: TestContext.CancellationToken); testHostResult.AssertExitCodeIs(ExitCodes.ZeroTests); - - string output = testHostResult.StandardOutput; - Assert.IsFalse(output.Contains("Canceling the test session")); + testHostResult.AssertOutputDoesNotContain("Canceling the test session"); } public sealed class TestAssetFixture() : TestAssetFixtureBase(AcceptanceFixture.NuGetGlobalPackagesFolder) @@ -163,4 +157,6 @@ public Task ExecuteRequestAsync(ExecuteRequestContext context) .PatchCodeWithReplace("$MicrosoftTestingPlatformVersion$", MicrosoftTestingPlatformVersion)); } } + + public TestContext TestContext { get; set; } } diff --git a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/TrxTests.cs b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/TrxTests.cs index 85bda001e8..26fbd380b1 100644 --- a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/TrxTests.cs +++ b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/TrxTests.cs @@ -11,7 +11,7 @@ public class TrxTests : AcceptanceTestBase public async Task Trx_WhenReportTrxIsNotSpecified_TrxReportIsNotGenerated(string tfm) { var testHost = TestInfrastructure.TestHost.LocateFrom(AssetFixture.TargetAssetPath, TestAssetFixture.AssetName, tfm); - TestHostResult testHostResult = await testHost.ExecuteAsync(); + TestHostResult testHostResult = await testHost.ExecuteAsync(cancellationToken: TestContext.CancellationToken); testHostResult.AssertExitCodeIs(ExitCodes.Success); @@ -30,7 +30,7 @@ public async Task Trx_WhenReportTrxIsSpecified_TrxReportIsGeneratedInDefaultLoca string trxPathPattern = Path.Combine(testResultsPath, ".*.trx").Replace(@"\", @"\\"); var testHost = TestInfrastructure.TestHost.LocateFrom(AssetFixture.TargetAssetPath, TestAssetFixture.AssetName, tfm); - TestHostResult testHostResult = await testHost.ExecuteAsync("--report-trx"); + TestHostResult testHostResult = await testHost.ExecuteAsync("--report-trx", cancellationToken: TestContext.CancellationToken); // number of test is the third param because we have two different test code with different number of tests. await AssertTrxReportWasGeneratedAsync(testHostResult, trxPathPattern, 1); @@ -50,14 +50,14 @@ public async Task Trx_WhenTestHostCrash_ErrorIsDisplayedInsideTheTrx(string tfm) var testHost = TestInfrastructure.TestHost.LocateFrom(AssetFixture.TargetAssetPath, TestAssetFixture.AssetName, tfm); TestHostResult testHostResult = await testHost.ExecuteAsync( $"--crashdump --report-trx --report-trx-filename {fileName}.trx", - new() { { "CRASHPROCESS", "1" } }); + new() { { "CRASHPROCESS", "1" } }, cancellationToken: TestContext.CancellationToken); testHostResult.AssertExitCodeIs(ExitCodes.TestHostProcessExitedNonGracefully); string trxFile = Directory.GetFiles(testHost.DirectoryName, $"{fileName}.trx", SearchOption.AllDirectories).Single(); string trxContent = File.ReadAllText(trxFile); Assert.IsTrue(Regex.IsMatch(trxContent, @"Test host process pid: .* crashed\."), trxContent); - StringAssert.Contains(trxContent, """""", trxContent); + Assert.Contains("""""", trxContent, trxContent); } [DynamicData(nameof(TargetFrameworks.NetForDynamicData), typeof(TargetFrameworks))] @@ -66,21 +66,19 @@ public async Task Trx_WhenSkipTest_ItAppearsAsExpectedInsideTheTrx(string tfm) { string fileName = Guid.NewGuid().ToString("N"); var testHost = TestInfrastructure.TestHost.LocateFrom(AssetFixture.TargetAssetPathWithSkippedTest, TestAssetFixture.AssetNameUsingMSTest, tfm); - TestHostResult testHostResult = await testHost.ExecuteAsync($"--report-trx --report-trx-filename {fileName}.trx"); + TestHostResult testHostResult = await testHost.ExecuteAsync($"--report-trx --report-trx-filename {fileName}.trx", cancellationToken: TestContext.CancellationToken); testHostResult.AssertExitCodeIs(ExitCodes.ZeroTests); string trxFile = Directory.GetFiles(testHost.DirectoryName, $"{fileName}.trx", SearchOption.AllDirectories).Single(); string trxContent = File.ReadAllText(trxFile); - - // check if the tests have been added to Results, TestDefinitions, TestEntries and ResultSummary. - StringAssert.Contains(trxContent, @"""", trxContent); - StringAssert.Contains(trxContent, """""", trxContent); + Assert.Contains(@"""", trxContent, trxContent); + Assert.Contains("""""", trxContent, trxContent); } [DynamicData(nameof(TargetFrameworks.NetForDynamicData), typeof(TargetFrameworks))] @@ -91,7 +89,7 @@ public async Task Trx_WhenTheTestNameHasInvalidXmlChar_TheTrxCreatedSuccessfully string trxPathPattern = Path.Combine(testResultsPath, ".*.trx").Replace(@"\", @"\\"); var testHost = TestInfrastructure.TestHost.LocateFrom(AssetFixture.TargetAssetPathWithDataRow, TestAssetFixture.AssetNameUsingMSTest, tfm); - TestHostResult testHostResult = await testHost.ExecuteAsync("--report-trx"); + TestHostResult testHostResult = await testHost.ExecuteAsync("--report-trx", cancellationToken: TestContext.CancellationToken); // number of test is the third param because we have two different test code with different number of tests. await AssertTrxReportWasGeneratedAsync(testHostResult, trxPathPattern, 2); @@ -103,7 +101,7 @@ public async Task Trx_UsingDataDriven_CreatesUnitTestTagForEachOneInsideTheTrx(s { string fileName = Guid.NewGuid().ToString("N"); var testHost = TestInfrastructure.TestHost.LocateFrom(AssetFixture.TargetAssetPathWithSkippedTest, TestAssetFixture.AssetNameUsingMSTest, tfm); - TestHostResult testHostResult = await testHost.ExecuteAsync($"--report-trx --report-trx-filename {fileName}.trx"); + TestHostResult testHostResult = await testHost.ExecuteAsync($"--report-trx --report-trx-filename {fileName}.trx", cancellationToken: TestContext.CancellationToken); testHostResult.AssertExitCodeIs(ExitCodes.ZeroTests); @@ -128,7 +126,7 @@ public async Task Trx_WhenReportTrxIsSpecifiedWithFullPath_TrxReportShouldFail(s Assert.IsFalse(Directory.Exists(testResultsPath)); var testHost = TestInfrastructure.TestHost.LocateFrom(AssetFixture.TargetAssetPath, TestAssetFixture.AssetName, tfm); - TestHostResult testHostResult = await testHost.ExecuteAsync($"--report-trx --report-trx-filename {Path.Combine(testResultsPath, "report.trx")}"); + TestHostResult testHostResult = await testHost.ExecuteAsync($"--report-trx --report-trx-filename {Path.Combine(testResultsPath, "report.trx")}", cancellationToken: TestContext.CancellationToken); testHostResult.AssertExitCodeIs(ExitCodes.InvalidCommandLine); testHostResult.AssertOutputContains("Option '--report-trx-filename' has invalid arguments: file name argument must not contain path (e.g. --report-trx-filename myreport.trx)"); @@ -139,7 +137,7 @@ public async Task Trx_WhenReportTrxIsSpecifiedWithFullPath_TrxReportShouldFail(s public async Task Trx_WhenReportTrxIsSpecifiedWithRelativePath_TrxReportShouldFail(string tfm) { var testHost = TestInfrastructure.TestHost.LocateFrom(AssetFixture.TargetAssetPath, TestAssetFixture.AssetName, tfm); - TestHostResult testHostResult = await testHost.ExecuteAsync($"--report-trx --report-trx-filename {Path.Combine("aaa", "report.trx")}"); + TestHostResult testHostResult = await testHost.ExecuteAsync($"--report-trx --report-trx-filename {Path.Combine("aaa", "report.trx")}", cancellationToken: TestContext.CancellationToken); testHostResult.AssertExitCodeIs(ExitCodes.InvalidCommandLine); testHostResult.AssertOutputContains("Option '--report-trx-filename' has invalid arguments: file name argument must not contain path (e.g. --report-trx-filename myreport.trx)"); @@ -150,7 +148,7 @@ public async Task Trx_WhenReportTrxIsSpecifiedWithRelativePath_TrxReportShouldFa public async Task Trx_WhenReportTrxIsNotSpecifiedAndReportTrxPathIsSpecified_ErrorIsDisplayed(string tfm) { var testHost = TestInfrastructure.TestHost.LocateFrom(AssetFixture.TargetAssetPath, TestAssetFixture.AssetName, tfm); - TestHostResult testHostResult = await testHost.ExecuteAsync("--report-trx-filename report.trx"); + TestHostResult testHostResult = await testHost.ExecuteAsync("--report-trx-filename report.trx", cancellationToken: TestContext.CancellationToken); testHostResult.AssertExitCodeIs(ExitCodes.InvalidCommandLine); testHostResult.AssertOutputContains("Error: '--report-trx-filename' requires '--report-trx' to be enabled"); @@ -162,13 +160,13 @@ public async Task Trx_WhenReportTrxIsSpecifiedAndReportTrxPathIsSpecified_Overwr { var testHost = TestInfrastructure.TestHost.LocateFrom(AssetFixture.TargetAssetPath, TestAssetFixture.AssetName, tfm); string reportFileName = $"report-{tfm}.trx"; - TestHostResult testHostResult = await testHost.ExecuteAsync($"--report-trx --report-trx-filename {reportFileName}"); + TestHostResult testHostResult = await testHost.ExecuteAsync($"--report-trx --report-trx-filename {reportFileName}", cancellationToken: TestContext.CancellationToken); testHostResult.AssertExitCodeIs(ExitCodes.Success); string warningMessage = $"Warning: Trx file '{Path.Combine(testHost.DirectoryName, "TestResults", reportFileName)}' already exists and will be overwritten."; testHostResult.AssertOutputDoesNotContain(warningMessage); - testHostResult = await testHost.ExecuteAsync($"--report-trx --report-trx-filename {reportFileName}"); + testHostResult = await testHost.ExecuteAsync($"--report-trx --report-trx-filename {reportFileName}", cancellationToken: TestContext.CancellationToken); testHostResult.AssertExitCodeIs(ExitCodes.Success); testHostResult.AssertOutputContains(warningMessage); @@ -179,7 +177,7 @@ public async Task Trx_WhenReportTrxIsSpecifiedAndReportTrxPathIsSpecified_Overwr public async Task Trx_WhenReportTrxIsSpecifiedAndListTestsIsSpecified_ErrorIsDisplayed(string tfm) { var testHost = TestInfrastructure.TestHost.LocateFrom(AssetFixture.TargetAssetPath, TestAssetFixture.AssetName, tfm); - TestHostResult testHostResult = await testHost.ExecuteAsync("--report-trx --list-tests"); + TestHostResult testHostResult = await testHost.ExecuteAsync("--report-trx --list-tests", cancellationToken: TestContext.CancellationToken); testHostResult.AssertExitCodeIs(ExitCodes.InvalidCommandLine); testHostResult.AssertOutputContains("Error: '--report-trx' cannot be enabled when using '--list-tests'"); @@ -384,4 +382,6 @@ public void TestMethod1(string s) .PatchCodeWithReplace("$IgnoreTestAttributeOrNothing$", string.Empty)); } } + + public TestContext TestContext { get; set; } } diff --git a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/TypeForwardingTests.cs b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/TypeForwardingTests.cs index 4a8e77822b..b39ea87d09 100644 --- a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/TypeForwardingTests.cs +++ b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/TypeForwardingTests.cs @@ -68,12 +68,14 @@ public async Task SettingDisplayNameFromNetStandardLibraryDuringNetCurrentRuntim .PatchCodeWithReplace("$MicrosoftTestingPlatformVersion$", MicrosoftTestingPlatformVersion); using TestAsset testAsset = await TestAsset.GenerateAssetAsync(AssetName, patchedSources); - await DotnetCli.RunAsync($"build -m:1 -nodeReuse:false {testAsset.TargetAssetPath}/ConsoleApp -c Release", AcceptanceFixture.NuGetGlobalPackagesFolder.Path); + await DotnetCli.RunAsync($"build -m:1 -nodeReuse:false {testAsset.TargetAssetPath}/ConsoleApp -c Release", AcceptanceFixture.NuGetGlobalPackagesFolder.Path, cancellationToken: TestContext.CancellationToken); var testHost = TestInfrastructure.TestHost.LocateFrom($"{testAsset.TargetAssetPath}/ConsoleApp", "ConsoleApp", TargetFrameworks.NetCurrent); - TestHostResult testHostResult = await testHost.ExecuteAsync(); + TestHostResult testHostResult = await testHost.ExecuteAsync(cancellationToken: TestContext.CancellationToken); testHostResult.AssertExitCodeIs(ExitCodes.Success); testHostResult.AssertOutputContains("MyDisplayName"); } + + public TestContext TestContext { get; set; } } diff --git a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/UnhandledExceptionPolicyTests.cs b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/UnhandledExceptionPolicyTests.cs index dcfe88cffe..b1675e268f 100644 --- a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/UnhandledExceptionPolicyTests.cs +++ b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/UnhandledExceptionPolicyTests.cs @@ -15,6 +15,8 @@ public enum Mode Default, } + public TestContext TestContext { get; set; } + internal static IEnumerable<(Mode Mode, string Arguments)> ModeProvider() { foreach (string tfm in TargetFrameworks.All) @@ -36,46 +38,52 @@ public async Task UnhandledExceptionPolicy_ConfigFile_UnobservedTaskException_Sh await clone.CopyDirectoryAsync(testHost.DirectoryName, clone.Path, retainAttributes: !RuntimeInformation.IsOSPlatform(OSPlatform.Windows)); testHost = TestInfrastructure.TestHost.LocateFrom(clone.Path, "UnhandledExceptionPolicyTests"); string configFileName = Path.Combine(testHost.DirectoryName, "UnhandledExceptionPolicyTests.testconfig.json"); - string contentFile = await File.ReadAllTextAsync(Path.Combine(testHost.DirectoryName, "UnhandledExceptionPolicyTests.testconfig.json")); + string contentFile = await File.ReadAllTextAsync(Path.Combine(testHost.DirectoryName, "UnhandledExceptionPolicyTests.testconfig.json"), TestContext.CancellationToken); TestHostResult? testHostResult; switch (mode) { case Mode.Enabled: File.WriteAllText(configFileName, contentFile.Replace("\"exitProcessOnUnhandledException\": false", "\"exitProcessOnUnhandledException\": true")); - testHostResult = await testHost.ExecuteAsync(null, new() { { "UNOBSERVEDTASKEXCEPTION", "1" } }); + testHostResult = await testHost.ExecuteAsync(null, new() { { "UNOBSERVEDTASKEXCEPTION", "1" } }, cancellationToken: TestContext.CancellationToken); testHostResult.AssertExitCodeIsNot(ExitCodes.Success); testHostResult.AssertOutputContains("[UnhandledExceptionHandler.OnTaskSchedulerUnobservedTaskException(testhost controller workflow)]"); break; case Mode.Disabled: File.WriteAllText(configFileName, contentFile.Replace("\"exitProcessOnUnhandledException\": false", "\"exitProcessOnUnhandledException\": false")); - testHostResult = await testHost.ExecuteAsync(null, new() { { "UNOBSERVEDTASKEXCEPTION", "1" } }); + testHostResult = await testHost.ExecuteAsync(null, new() { { "UNOBSERVEDTASKEXCEPTION", "1" } }, cancellationToken: TestContext.CancellationToken); testHostResult.AssertExitCodeIs(ExitCodes.Success); testHostResult.AssertOutputDoesNotContain("[UnhandledExceptionHandler.OnTaskSchedulerUnobservedTaskException]"); break; case Mode.Default: File.Delete(configFileName); - testHostResult = await testHost.ExecuteAsync(null, new() { { "UNOBSERVEDTASKEXCEPTION", "1" } }); + testHostResult = await testHost.ExecuteAsync(null, new() { { "UNOBSERVEDTASKEXCEPTION", "1" } }, cancellationToken: TestContext.CancellationToken); testHostResult.AssertExitCodeIs(ExitCodes.Success); testHostResult.AssertOutputDoesNotContain("[UnhandledExceptionHandler.OnTaskSchedulerUnobservedTaskException]"); break; case Mode.DisabledByEnvironmentVariable: File.WriteAllText(configFileName, contentFile.Replace("\"exitProcessOnUnhandledException\": false", "\"exitProcessOnUnhandledException\": true")); - testHostResult = await testHost.ExecuteAsync(null, new() - { - { "UNOBSERVEDTASKEXCEPTION", "1" }, - { EnvironmentVariableConstants.TESTINGPLATFORM_EXIT_PROCESS_ON_UNHANDLED_EXCEPTION, "0" }, - }); + testHostResult = await testHost.ExecuteAsync( + null, + new() + { + { "UNOBSERVEDTASKEXCEPTION", "1" }, + { EnvironmentVariableConstants.TESTINGPLATFORM_EXIT_PROCESS_ON_UNHANDLED_EXCEPTION, "0" }, + }, + cancellationToken: TestContext.CancellationToken); testHostResult.AssertExitCodeIs(ExitCodes.Success); testHostResult.AssertOutputDoesNotContain("[UnhandledExceptionHandler.OnTaskSchedulerUnobservedTaskException]"); break; case Mode.EnabledByEnvironmentVariable: File.WriteAllText(configFileName, contentFile.Replace("\"exitProcessOnUnhandledException\": false", "\"exitProcessOnUnhandledException\": false")); - testHostResult = await testHost.ExecuteAsync(null, new() - { - { "UNOBSERVEDTASKEXCEPTION", "1" }, - { EnvironmentVariableConstants.TESTINGPLATFORM_EXIT_PROCESS_ON_UNHANDLED_EXCEPTION, "1" }, - }); + testHostResult = await testHost.ExecuteAsync( + null, + new() + { + { "UNOBSERVEDTASKEXCEPTION", "1" }, + { EnvironmentVariableConstants.TESTINGPLATFORM_EXIT_PROCESS_ON_UNHANDLED_EXCEPTION, "1" }, + }, + cancellationToken: TestContext.CancellationToken); testHostResult.AssertExitCodeIsNot(ExitCodes.Success); testHostResult.AssertOutputContains("[UnhandledExceptionHandler.OnTaskSchedulerUnobservedTaskException(testhost controller workflow)]"); break; @@ -93,47 +101,53 @@ public async Task UnhandledExceptionPolicy_EnvironmentVariable_UnhandledExceptio await clone.CopyDirectoryAsync(testHost.DirectoryName, clone.Path, retainAttributes: !RuntimeInformation.IsOSPlatform(OSPlatform.Windows)); testHost = TestInfrastructure.TestHost.LocateFrom(clone.Path, "UnhandledExceptionPolicyTests"); string configFileName = Path.Combine(testHost.DirectoryName, "UnhandledExceptionPolicyTests.testconfig.json"); - string contentFile = await File.ReadAllTextAsync(Path.Combine(testHost.DirectoryName, "UnhandledExceptionPolicyTests.testconfig.json")); + string contentFile = await File.ReadAllTextAsync(Path.Combine(testHost.DirectoryName, "UnhandledExceptionPolicyTests.testconfig.json"), TestContext.CancellationToken); TestHostResult? testHostResult; switch (mode) { case Mode.Enabled: File.WriteAllText(configFileName, contentFile.Replace("\"exitProcessOnUnhandledException\": false", "\"exitProcessOnUnhandledException\": true")); - testHostResult = await testHost.ExecuteAsync(null, new() { { "UNHANDLEDEXCEPTION", "1" } }); + testHostResult = await testHost.ExecuteAsync(null, new() { { "UNHANDLEDEXCEPTION", "1" } }, cancellationToken: TestContext.CancellationToken); testHostResult.AssertOutputContains("[UnhandledExceptionHandler.OnCurrentDomainUnhandledException(testhost controller workflow)]"); testHostResult.AssertOutputContains("IsTerminating: True"); testHostResult.AssertExitCodeIsNot(ExitCodes.Success); break; case Mode.Disabled: File.WriteAllText(configFileName, contentFile.Replace("\"exitProcessOnUnhandledException\": false", "\"exitProcessOnUnhandledException\": false")); - testHostResult = await testHost.ExecuteAsync(null, new() { { "UNHANDLEDEXCEPTION", "1" } }); + testHostResult = await testHost.ExecuteAsync(null, new() { { "UNHANDLEDEXCEPTION", "1" } }, cancellationToken: TestContext.CancellationToken); Assert.IsTrue(testHostResult.StandardError.Contains("Unhandled exception", StringComparison.OrdinalIgnoreCase), testHostResult.ToString()); testHostResult.AssertExitCodeIsNot(ExitCodes.Success); break; case Mode.Default: File.Delete(configFileName); - testHostResult = await testHost.ExecuteAsync(null, new() { { "UNHANDLEDEXCEPTION", "1" } }); + testHostResult = await testHost.ExecuteAsync(null, new() { { "UNHANDLEDEXCEPTION", "1" } }, cancellationToken: TestContext.CancellationToken); testHostResult.AssertExitCodeIsNot(ExitCodes.Success); break; case Mode.DisabledByEnvironmentVariable: File.WriteAllText(configFileName, contentFile.Replace("\"exitProcessOnUnhandledException\": false", "\"exitProcessOnUnhandledException\": true")); - testHostResult = await testHost.ExecuteAsync(null, new() - { - { "UNHANDLEDEXCEPTION", "1" }, - { EnvironmentVariableConstants.TESTINGPLATFORM_EXIT_PROCESS_ON_UNHANDLED_EXCEPTION, "0" }, - }); + testHostResult = await testHost.ExecuteAsync( + null, + new() + { + { "UNHANDLEDEXCEPTION", "1" }, + { EnvironmentVariableConstants.TESTINGPLATFORM_EXIT_PROCESS_ON_UNHANDLED_EXCEPTION, "0" }, + }, + cancellationToken: TestContext.CancellationToken); Assert.IsTrue(testHostResult.StandardError.Contains("Unhandled exception", StringComparison.OrdinalIgnoreCase), testHostResult.ToString()); testHostResult.AssertOutputDoesNotContain("IsTerminating: True"); testHostResult.AssertExitCodeIsNot(ExitCodes.Success); break; case Mode.EnabledByEnvironmentVariable: File.WriteAllText(configFileName, contentFile.Replace("\"exitProcessOnUnhandledException\": false", "\"exitProcessOnUnhandledException\": false")); - testHostResult = await testHost.ExecuteAsync(null, new() - { - { "UNHANDLEDEXCEPTION", "1" }, - { EnvironmentVariableConstants.TESTINGPLATFORM_EXIT_PROCESS_ON_UNHANDLED_EXCEPTION, "1" }, - }); + testHostResult = await testHost.ExecuteAsync( + null, + new() + { + { "UNHANDLEDEXCEPTION", "1" }, + { EnvironmentVariableConstants.TESTINGPLATFORM_EXIT_PROCESS_ON_UNHANDLED_EXCEPTION, "1" }, + }, + cancellationToken: TestContext.CancellationToken); testHostResult.AssertOutputContains("[UnhandledExceptionHandler.OnCurrentDomainUnhandledException(testhost controller workflow)]"); testHostResult.AssertOutputContains("IsTerminating: True"); testHostResult.AssertExitCodeIsNot(ExitCodes.Success); diff --git a/test/IntegrationTests/PlatformServices.Desktop.IntegrationTests/DesktopTestSourceHostTests.cs b/test/IntegrationTests/PlatformServices.Desktop.IntegrationTests/DesktopTestSourceHostTests.cs index 793d570a5a..036e595186 100644 --- a/test/IntegrationTests/PlatformServices.Desktop.IntegrationTests/DesktopTestSourceHostTests.cs +++ b/test/IntegrationTests/PlatformServices.Desktop.IntegrationTests/DesktopTestSourceHostTests.cs @@ -1,7 +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 FluentAssertions; +using AwesomeAssertions; using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices; using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Utilities; diff --git a/test/IntegrationTests/PlatformServices.Desktop.IntegrationTests/ReflectionUtilityTests.cs b/test/IntegrationTests/PlatformServices.Desktop.IntegrationTests/ReflectionUtilityTests.cs index 7c91950519..135c8f072f 100644 --- a/test/IntegrationTests/PlatformServices.Desktop.IntegrationTests/ReflectionUtilityTests.cs +++ b/test/IntegrationTests/PlatformServices.Desktop.IntegrationTests/ReflectionUtilityTests.cs @@ -1,7 +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 FluentAssertions; +using AwesomeAssertions; using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Utilities; @@ -174,9 +174,9 @@ public void GetSpecificCustomAttributesShouldReturnAllAttributesIncludingUserDef IReadOnlyList attributes = ReflectionUtility.GetCustomAttributes(methodInfo, typeof(TestPropertyAttribute), true); attributes.Should().NotBeNull(); - attributes.Should().HaveCount(1); + attributes.Should().HaveCount(2); - string[] expectedAttributes = ["Duration : superfast"]; + string[] expectedAttributes = ["Duration : superfast", "Owner : base"]; GetAttributeValuePairs(attributes).Should().Equal(expectedAttributes); } @@ -263,7 +263,7 @@ private static string[] GetAttributeValuePairs(IEnumerable attributes) } else if (attribute is TestCategoryAttribute categoryAttribute) { - attributeValuePairs.Add("TestCategory : " + categoryAttribute.TestCategories.Aggregate((i, j) => i + "," + j)); + attributeValuePairs.Add("TestCategory : " + categoryAttribute.TestCategories.Aggregate((i, j) => i + ',' + j)); } else if (attribute is DurationAttribute durationAttribute) { @@ -271,10 +271,10 @@ private static string[] GetAttributeValuePairs(IEnumerable attributes) } else if (attribute is CategoryArrayAttribute arrayAttribute) { - attributeValuePairs.Add("CategoryAttribute : " + arrayAttribute.Value.Aggregate((i, j) => i + "," + j)); + attributeValuePairs.Add("CategoryAttribute : " + arrayAttribute.Value.Aggregate((i, j) => i + ',' + j)); } } - return attributeValuePairs.ToArray(); + return [.. attributeValuePairs]; } } diff --git a/test/IntegrationTests/TestAssets/DataRowTestProject/DataRowTests_OverriddenGetDisplayName.cs b/test/IntegrationTests/TestAssets/DataRowTestProject/DataRowTests_OverriddenGetDisplayName.cs index b8c2e49da3..bcd6a6bffc 100644 --- a/test/IntegrationTests/TestAssets/DataRowTestProject/DataRowTests_OverriddenGetDisplayName.cs +++ b/test/IntegrationTests/TestAssets/DataRowTestProject/DataRowTests_OverriddenGetDisplayName.cs @@ -34,7 +34,7 @@ public static IEnumerable Data { get { - yield return new object[] { "SomeData" }; + yield return ["SomeData"]; } } } diff --git a/test/IntegrationTests/TestAssets/DataRowTestProject/DataRowTests_Regular.cs b/test/IntegrationTests/TestAssets/DataRowTestProject/DataRowTests_Regular.cs index 6f6e1ae3ef..cf397b5972 100644 --- a/test/IntegrationTests/TestAssets/DataRowTestProject/DataRowTests_Regular.cs +++ b/test/IntegrationTests/TestAssets/DataRowTestProject/DataRowTests_Regular.cs @@ -68,7 +68,9 @@ public void NullValueInData(string email, string password, string returnUrl) public void NullValue(object o) => Assert.IsNull(o); [TestMethod] - [DataRow(new string[] { "" })] +#pragma warning disable SA1122 // Use string.Empty for empty strings + [DataRow([""])] +#pragma warning restore SA1122 // Use string.Empty for empty strings public void OneStringArray(string[] lines) => Assert.AreEqual(1, lines.Length); [TestMethod] @@ -80,7 +82,9 @@ public void TwoStringArrays(string[] input1, string[] input2) } [TestMethod] - [DataRow(new object[] { "", 1 })] +#pragma warning disable SA1122 // Use string.Empty for empty strings + [DataRow(["", 1])] +#pragma warning restore SA1122 // Use string.Empty for empty strings public void OneObjectArray(object[] objects) => Assert.AreEqual(2, objects.Length); [TestMethod] diff --git a/test/IntegrationTests/TestAssets/DynamicDataTestProject/DisableExpansionTests.cs b/test/IntegrationTests/TestAssets/DynamicDataTestProject/DisableExpansionTests.cs index c22a047e3f..97f8dc09f4 100644 --- a/test/IntegrationTests/TestAssets/DynamicDataTestProject/DisableExpansionTests.cs +++ b/test/IntegrationTests/TestAssets/DynamicDataTestProject/DisableExpansionTests.cs @@ -50,8 +50,8 @@ public void TestPropertyWithTwoSourcesAndSecondDisablesExpansion(int a, string s private static IEnumerable MethodSource() { - yield return new object[] { 1, "a" }; - yield return new object[] { 2, "b" }; + yield return [1, "a"]; + yield return [2, "b"]; } } @@ -61,7 +61,7 @@ public class DataSourceHelper public static IEnumerable MethodSource() { - yield return new object[] { 3, "c" }; - yield return new object[] { 4, "d" }; + yield return [3, "c"]; + yield return [4, "d"]; } } diff --git a/test/IntegrationTests/TestAssets/DynamicDataTestProject/DynamicDataTests.cs b/test/IntegrationTests/TestAssets/DynamicDataTestProject/DynamicDataTests.cs index 8fdeb44f88..27ebbf8590 100644 --- a/test/IntegrationTests/TestAssets/DynamicDataTestProject/DynamicDataTests.cs +++ b/test/IntegrationTests/TestAssets/DynamicDataTestProject/DynamicDataTests.cs @@ -253,14 +253,14 @@ public class ExampleTestCase private static IEnumerable StringAndInt32() { - yield return new object[] { "1", 1 }; - yield return new object[] { "2", 1 }; + yield return ["1", 1]; + yield return ["2", 1]; } private static IEnumerable Int32AndString() { - yield return new object[] { 1, "0" }; - yield return new object[] { 2, "2" }; + yield return [1, "0"]; + yield return [2, "2"]; } private static IEnumerable SimpleCollection @@ -272,4 +272,21 @@ private static IEnumerable SimpleCollection yield return 4; } } + + // Test field support - static field for dynamic data + private static readonly IEnumerable FieldTestData = new[] + { + ["field", 5], + new object[] { "test", 4 }, + }; + + [DataTestMethod] + [DynamicData(nameof(FieldTestData), DynamicDataSourceType.Field)] + public void DynamicDataTest_SourceFieldExplicit(string text, int expectedLength) + => Assert.AreEqual(expectedLength, text.Length); + + [DataTestMethod] + [DynamicData(nameof(FieldTestData))] // AutoDetect should find the field + public void DynamicDataTest_SourceFieldAutoDetect(string text, int expectedLength) + => Assert.AreEqual(expectedLength, text.Length); } diff --git a/test/IntegrationTests/TestAssets/FxExtensibilityTestProject/CustomTestExTests.cs b/test/IntegrationTests/TestAssets/FxExtensibilityTestProject/CustomTestExTests.cs index fe39499599..48d882ece8 100644 --- a/test/IntegrationTests/TestAssets/FxExtensibilityTestProject/CustomTestExTests.cs +++ b/test/IntegrationTests/TestAssets/FxExtensibilityTestProject/CustomTestExTests.cs @@ -53,7 +53,7 @@ public override TestResult[] Execute(ITestMethod testMethod) results.AddRange(testResults); } - return results.ToArray(); + return [.. results]; } } diff --git a/test/IntegrationTests/TestAssets/FxExtensibilityTestProject/TestDataSourceExTests.cs b/test/IntegrationTests/TestAssets/FxExtensibilityTestProject/TestDataSourceExTests.cs index 10dc340e8d..3f35f70cfb 100644 --- a/test/IntegrationTests/TestAssets/FxExtensibilityTestProject/TestDataSourceExTests.cs +++ b/test/IntegrationTests/TestAssets/FxExtensibilityTestProject/TestDataSourceExTests.cs @@ -39,7 +39,7 @@ 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; + => data != null ? $"{methodInfo.Name} ({string.Join(",", data)})" : null; } [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] @@ -48,7 +48,7 @@ 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; + => data != null ? $"{methodInfo.Name} ({string.Join(",", data)})" : null; } [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] @@ -59,5 +59,5 @@ public class CustomDisableExpansionTestDataSourceAttribute : Attribute, ITestDat 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; + => data != null ? $"{methodInfo.Name} ({string.Join(",", data)})" : null; } diff --git a/test/IntegrationTests/TestAssets/TestCategoriesFromTestDataRowProject/TestCategoriesFromTestDataRowProject.csproj b/test/IntegrationTests/TestAssets/TestCategoriesFromTestDataRowProject/TestCategoriesFromTestDataRowProject.csproj new file mode 100644 index 0000000000..05f68e1367 --- /dev/null +++ b/test/IntegrationTests/TestAssets/TestCategoriesFromTestDataRowProject/TestCategoriesFromTestDataRowProject.csproj @@ -0,0 +1,12 @@ + + + net462 + enable + enable + false + + + + + + diff --git a/test/IntegrationTests/TestAssets/TestCategoriesFromTestDataRowProject/TestCategoriesFromTestDataRowTests.cs b/test/IntegrationTests/TestAssets/TestCategoriesFromTestDataRowProject/TestCategoriesFromTestDataRowTests.cs new file mode 100644 index 0000000000..3bad685d52 --- /dev/null +++ b/test/IntegrationTests/TestAssets/TestCategoriesFromTestDataRowProject/TestCategoriesFromTestDataRowTests.cs @@ -0,0 +1,71 @@ +// 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 TestCategoriesFromTestDataRowProject; + +[TestClass] +public class TestCategoriesFromTestDataRowTests +{ + [TestMethod] + [DynamicData(nameof(GetTestDataWithCategories))] + public void TestMethodWithDynamicDataCategories(string value, int number) + { + Assert.IsTrue(!string.IsNullOrEmpty(value)); + Assert.IsTrue(number > 0); + } + + public static IEnumerable> GetTestDataWithCategories() + { + // Test data row with categories + yield return new TestDataRow<(string, int)>(("value1", 1)) + { + TestCategories = ["Integration", "Slow"], + DisplayName = "Test with Integration and Slow categories", + }; + + // Test data row with different categories + yield return new TestDataRow<(string, int)>(("value2", 2)) + { + TestCategories = ["Unit", "Fast"], + DisplayName = "Test with Unit and Fast categories", + }; + + // Test data row with no categories (should inherit from method level) + yield return new TestDataRow<(string, int)>(("value3", 3)) + { + DisplayName = "Test with no additional categories", + }; + } + + public static IEnumerable GetRegularTestData() + { + // Regular data row (not TestDataRow) - should work as before + yield return new object[] { "value4", 4 }; + } + + [TestMethod] + [DynamicData(nameof(GetRegularTestData))] + public void TestMethodWithRegularData(string value, int number) + { + Assert.IsTrue(!string.IsNullOrEmpty(value)); + Assert.IsTrue(number > 0); + } + + [TestCategory("MethodLevel")] + [TestMethod] + [DynamicData(nameof(GetTestDataWithCategoriesForMethodWithCategory))] + public void TestMethodWithMethodLevelCategoriesAndDataCategories(string value) + => Assert.IsTrue(!string.IsNullOrEmpty(value)); + + public static IEnumerable> GetTestDataWithCategoriesForMethodWithCategory() + { + // This should have both "MethodLevel" and "DataLevel" categories + yield return new TestDataRow("test") + { + TestCategories = ["DataLevel"], + DisplayName = "Test with method and data categories", + }; + } +} diff --git a/test/IntegrationTests/TestAssets/TestProject/TestProjectForDiscovery.csproj b/test/IntegrationTests/TestAssets/TestProject/TestProjectForDiscovery.csproj index c243ff45e2..47fb7b1742 100644 --- a/test/IntegrationTests/TestAssets/TestProject/TestProjectForDiscovery.csproj +++ b/test/IntegrationTests/TestAssets/TestProject/TestProjectForDiscovery.csproj @@ -5,7 +5,7 @@ - TRACE + $(DefineConstants);TRACE prompt 4 False diff --git a/test/IntegrationTests/TestAssets/TimeoutTestProject/TimeoutTestClass.cs b/test/IntegrationTests/TestAssets/TimeoutTestProject/TimeoutTestClass.cs index e2a9892a91..da1e4dcb05 100644 --- a/test/IntegrationTests/TestAssets/TimeoutTestProject/TimeoutTestClass.cs +++ b/test/IntegrationTests/TestAssets/TimeoutTestProject/TimeoutTestClass.cs @@ -5,6 +5,7 @@ namespace TimeoutTestProject; +#pragma warning disable CS0618 // Type or member is obsolete [TestClass] public class TimeoutTestClass { diff --git a/test/Performance/MSTest.Performance.Runner/Context.cs b/test/Performance/MSTest.Performance.Runner/Context.cs index a3bd69a829..ab4a7a76d3 100644 --- a/test/Performance/MSTest.Performance.Runner/Context.cs +++ b/test/Performance/MSTest.Performance.Runner/Context.cs @@ -5,13 +5,13 @@ namespace MSTest.Performance.Runner; internal class Context : IContext, IDisposable { - private List _disposables = new(); + private List _disposables = []; public IDictionary Properties { get; private set; } = new Dictionary(); public void Init(IDictionary properties) { - _disposables = new(); + _disposables = []; Properties = properties; } diff --git a/test/Performance/MSTest.Performance.Runner/PipelinesRunner.cs b/test/Performance/MSTest.Performance.Runner/PipelinesRunner.cs index bcc2153539..ad75d304d4 100644 --- a/test/Performance/MSTest.Performance.Runner/PipelinesRunner.cs +++ b/test/Performance/MSTest.Performance.Runner/PipelinesRunner.cs @@ -7,7 +7,7 @@ namespace MSTest.Performance.Runner; internal class PipelinesRunner { - private readonly List _pipelines = new(); + private readonly List _pipelines = []; public void AddPipeline(string groupName, string pipelineName, OSPlatform[] oSPlatform, Action> func, Action>? updatePropertyBag = null, string[]? traits = null) => _pipelines.Add(new PipelineInfo(groupName, pipelineName, oSPlatform, func, updatePropertyBag, traits)); diff --git a/test/Performance/MSTest.Performance.Runner/Steps/MoveFiles.cs b/test/Performance/MSTest.Performance.Runner/Steps/MoveFiles.cs index 6251d93fbc..aa1c4164f1 100644 --- a/test/Performance/MSTest.Performance.Runner/Steps/MoveFiles.cs +++ b/test/Performance/MSTest.Performance.Runner/Steps/MoveFiles.cs @@ -10,7 +10,7 @@ internal class MoveFiles : IStep private readonly DirectoryInfo _finalFolder; private readonly Matcher _matcher = new(); - public string Description => "Move files"; + public string Description => "MoveFile files"; public MoveFiles(string filter, string finalFolder) { diff --git a/test/Performance/MSTest.Performance.Runner/Steps/PlainProcess.cs b/test/Performance/MSTest.Performance.Runner/Steps/PlainProcess.cs index b1f1af50d9..691ffb6a74 100644 --- a/test/Performance/MSTest.Performance.Runner/Steps/PlainProcess.cs +++ b/test/Performance/MSTest.Performance.Runner/Steps/PlainProcess.cs @@ -36,7 +36,7 @@ public async Task ExecuteAsync(BuildArtifact payload, IContext context) Console.WriteLine($"Process command: '{processStartInfo.FileName} {processStartInfo.Arguments.Trim()}' for {_numberOfRun} times"); - List results = new(); + List results = []; for (int i = 0; i < _numberOfRun; i++) { using Process process = Process.Start(processStartInfo)!; diff --git a/test/UnitTests/MSTest.Analyzers.UnitTests/AssemblyCleanupShouldBeValidAnalyzerTests.cs b/test/UnitTests/MSTest.Analyzers.UnitTests/AssemblyCleanupShouldBeValidAnalyzerTests.cs index 44aba361df..7ca5537617 100644 --- a/test/UnitTests/MSTest.Analyzers.UnitTests/AssemblyCleanupShouldBeValidAnalyzerTests.cs +++ b/test/UnitTests/MSTest.Analyzers.UnitTests/AssemblyCleanupShouldBeValidAnalyzerTests.cs @@ -261,6 +261,7 @@ public static void AssemblyCleanup(TestContext testContext) await VerifyCS.VerifyCodeFixAsync(code, code); } +#if NET [TestMethod] public async Task WhenAssemblyCleanupReturnTypeIsNotValid_Diagnostic() { @@ -400,6 +401,7 @@ public static ValueTask AssemblyCleanup2() await VerifyCS.VerifyAnalyzerAsync(code); } +#endif [TestMethod] public async Task WhenAssemblyCleanupIsAsyncVoid_Diagnostic() diff --git a/test/UnitTests/MSTest.Analyzers.UnitTests/AssemblyInitializeShouldBeValidAnalyzerTests.cs b/test/UnitTests/MSTest.Analyzers.UnitTests/AssemblyInitializeShouldBeValidAnalyzerTests.cs index e7ffa9a0fe..48acceee09 100644 --- a/test/UnitTests/MSTest.Analyzers.UnitTests/AssemblyInitializeShouldBeValidAnalyzerTests.cs +++ b/test/UnitTests/MSTest.Analyzers.UnitTests/AssemblyInitializeShouldBeValidAnalyzerTests.cs @@ -277,6 +277,7 @@ await VerifyCS.VerifyCodeFixAsync( fixedCode); } +#if NET [TestMethod] public async Task WhenAssemblyInitializeReturnTypeIsNotValid_Diagnostic() { @@ -412,6 +413,7 @@ public static ValueTask AssemblyInitialize2(TestContext testContext) await VerifyCS.VerifyAnalyzerAsync(code); } +#endif [TestMethod] public async Task WhenAssemblyInitializeIsAsyncVoid_Diagnostic() diff --git a/test/UnitTests/MSTest.Analyzers.UnitTests/AssertThrowsShouldContainSingleStatementAnalyzerTests.cs b/test/UnitTests/MSTest.Analyzers.UnitTests/AssertThrowsShouldContainSingleStatementAnalyzerTests.cs new file mode 100644 index 0000000000..26c4c0754b --- /dev/null +++ b/test/UnitTests/MSTest.Analyzers.UnitTests/AssertThrowsShouldContainSingleStatementAnalyzerTests.cs @@ -0,0 +1,605 @@ +// 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.AssertThrowsShouldContainSingleStatementAnalyzer, + Microsoft.CodeAnalysis.Testing.EmptyCodeFixProvider>; + +using VerifyVB = MSTest.Analyzers.Test.VisualBasicCodeFixVerifier< + MSTest.Analyzers.AssertThrowsShouldContainSingleStatementAnalyzer, + Microsoft.CodeAnalysis.Testing.EmptyCodeFixProvider>; + +namespace MSTest.Analyzers.Test; + +[TestClass] +public sealed class AssertThrowsShouldContainSingleStatementAnalyzerTests +{ + [TestMethod] + public async Task WhenAssertThrowsContainsMultipleStatements_CSharp_Diagnostic() + { + string code = """ + using System; + using System.Threading.Tasks; + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + // Multiple statements in Assert.Throws - should be flagged + [|Assert.Throws(() => + { + Console.WriteLine("First"); + Console.WriteLine("Second"); + })|]; + + // Multiple statements in Assert.Throws, wrapped in extra block syntax - should be flagged + [|Assert.Throws(() => + { + { + { + Console.WriteLine("First"); + } + { + Console.WriteLine("Second"); + } + } + })|]; + + // Multiple statements in Assert.ThrowsExactly - should be flagged + [|Assert.ThrowsExactly(() => + { + Console.WriteLine("First"); + Console.WriteLine("Second"); + })|]; + } + + [TestMethod] + public async Task MyAsyncTestMethod() + { + // Multiple statements in Assert.ThrowsAsync - should be flagged + [|Assert.ThrowsAsync(() => + { + Console.WriteLine("First"); + return Task.CompletedTask; + })|]; + + // Multiple statements in Assert.ThrowsExactlyAsync - should be flagged + [|Assert.ThrowsExactlyAsync(() => + { + Console.WriteLine("First"); + return Task.CompletedTask; + })|]; + } + } + """; + + await VerifyCS.VerifyAnalyzerAsync(code); + } + + [TestMethod] + public async Task WhenAssertThrowsContainsMultipleStatements_VB_Diagnostic() + { + string code = """ + Imports System + Imports System.Threading.Tasks + Imports Microsoft.VisualStudio.TestTools.UnitTesting + + + Public Class MyTestClass + + Public Sub MyTestMethod() + ' Multiple statements in Assert.Throws - should be flagged + [|Assert.Throws(Of Exception)(Sub() + Console.WriteLine("First") + Console.WriteLine("Second") + End Sub)|] + + ' Multiple statements in Assert.ThrowsExactly - should be flagged + [|Assert.ThrowsExactly(Of Exception)(Sub() + Console.WriteLine("First") + Console.WriteLine("Second") + End Sub)|] + End Sub + + + Public Async Function MyAsyncTestMethod() As Task + ' Multiple statements in Assert.ThrowsAsync - should be flagged + [|Assert.ThrowsAsync(Of Exception)(Function() + Console.WriteLine("First") + Return Task.CompletedTask + End Function)|] + + ' Multiple statements in Assert.ThrowsExactlyAsync - should be flagged + [|Assert.ThrowsExactlyAsync(Of Exception)(Function() + Console.WriteLine("First") + Return Task.CompletedTask + End Function)|] + End Function + End Class + """; + + await VerifyVB.VerifyAnalyzerAsync(code); + } + + [TestMethod] + public async Task WhenAssertThrowsContainsMultipleStatementsWithVariableDeclarations_CSharp_Diagnostic() + { + string code = """ + using System; + using System.Threading.Tasks; + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + // Multiple statements including variable declarations - should be flagged + [|Assert.Throws(() => + { + var value = 42; + Console.WriteLine(value); + DoSomething(); + })|]; + + // Multiple statements with ThrowsExactly - should be flagged + [|Assert.ThrowsExactly(() => + { + var value = 42; + Console.WriteLine(value); + DoSomething(); + })|]; + } + + [TestMethod] + public async Task MyAsyncTestMethod() + { + // Multiple statements with ThrowsAsync - should be flagged + [|Assert.ThrowsAsync(() => + { + var value = 42; + Console.WriteLine(value); + return Task.CompletedTask; + })|]; + + // Multiple statements with ThrowsExactlyAsync - should be flagged + [|Assert.ThrowsExactlyAsync(() => + { + var value = 42; + Console.WriteLine(value); + return Task.CompletedTask; + })|]; + } + + private static void DoSomething() => throw new Exception(); + } + """; + + await VerifyCS.VerifyAnalyzerAsync(code); + } + + [TestMethod] + public async Task WhenAssertThrowsContainsMultipleStatementsWithVariableDeclarations_VB_Diagnostic() + { + string code = """ + Imports System + Imports System.Threading.Tasks + Imports Microsoft.VisualStudio.TestTools.UnitTesting + + + Public Class MyTestClass + + Public Sub MyTestMethod() + ' Multiple statements including variable declarations - should be flagged + [|Assert.Throws(Of Exception)(Sub() + Dim value As Integer = 42 + Console.WriteLine(value) + DoSomething() + End Sub)|] + + ' Multiple statements with ThrowsExactly - should be flagged + [|Assert.ThrowsExactly(Of Exception)(Sub() + Dim value As Integer = 42 + Console.WriteLine(value) + DoSomething() + End Sub)|] + End Sub + + + Public Async Function MyAsyncTestMethod() As Task + ' Multiple statements with ThrowsAsync - should be flagged + [|Assert.ThrowsAsync(Of Exception)(Function() + Dim value As Integer = 42 + Console.WriteLine(value) + Return Task.CompletedTask + End Function)|] + + ' Multiple statements with ThrowsExactlyAsync - should be flagged + [|Assert.ThrowsExactlyAsync(Of Exception)(Function() + Dim value As Integer = 42 + Console.WriteLine(value) + Return Task.CompletedTask + End Function)|] + End Function + + Private Shared Sub DoSomething() + Throw New Exception() + End Sub + End Class + """; + + await VerifyVB.VerifyAnalyzerAsync(code); + } + + [TestMethod] + public async Task WhenAssertThrowsContainsSingleStatement_CSharp_NoDiagnostic() + { + string code = """ + using System; + using System.Threading.Tasks; + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + // Single statement - should NOT be flagged + Assert.Throws(() => Console.WriteLine("Only one")); + + // Single expression - should NOT be flagged + Assert.Throws(() => DoSomething()); + + // Single statement in block - should NOT be flagged + Assert.Throws(() => + { + DoSomething(); + }); + + // Single statement with ThrowsExactly - should NOT be flagged + Assert.ThrowsExactly(() => DoSomething()); + } + + [TestMethod] + public async Task MyAsyncTestMethod() + { + // Single async statement - should NOT be flagged + await Assert.ThrowsAsync(() => Task.CompletedTask); + + // Single async statement in block - should NOT be flagged + await Assert.ThrowsAsync(() => + { + return Task.CompletedTask; + }); + + // Single async statement with ThrowsExactlyAsync - should NOT be flagged + await Assert.ThrowsExactlyAsync(() => Task.CompletedTask); + } + + private static void DoSomething() => throw new Exception(); + } + """; + + await VerifyCS.VerifyAnalyzerAsync(code); + } + + [TestMethod] + public async Task WhenAssertThrowsContainsSingleStatement_VB_NoDiagnostic() + { + string code = """ + Imports System + Imports System.Threading.Tasks + Imports Microsoft.VisualStudio.TestTools.UnitTesting + + + Public Class MyTestClass + + Public Sub MyTestMethod() + ' Single statement - should NOT be flagged + Assert.Throws(Of Exception)(Sub() Console.WriteLine("Only one")) + + ' Single expression - should NOT be flagged + Assert.Throws(Of Exception)(Sub() DoSomething()) + + ' Single statement in block - should NOT be flagged + Assert.Throws(Of Exception)(Sub() + DoSomething() + End Sub) + + ' Single statement with ThrowsExactly - should NOT be flagged + Assert.ThrowsExactly(Of Exception)(Sub() DoSomething()) + End Sub + + + Public Async Function MyAsyncTestMethod() As Task + ' Single async statement - should NOT be flagged + Await Assert.ThrowsAsync(Of Exception)(Function() Task.CompletedTask) + + ' Single async statement in block - should NOT be flagged + Await Assert.ThrowsAsync(Of Exception)(Function() + Return Task.CompletedTask + End Function) + + ' Single async statement with ThrowsExactlyAsync - should NOT be flagged + Await Assert.ThrowsExactlyAsync(Of Exception)(Function() Task.CompletedTask) + End Function + + Private Shared Sub DoSomething() + Throw New Exception() + End Sub + End Class + """; + + await VerifyVB.VerifyAnalyzerAsync(code); + } + + [TestMethod] + public async Task WhenUsingOtherAssertMethods_CSharp_NoDiagnostic() + { + string code = """ + using System; + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + // Other Assert methods should not be flagged even with multiple statements + Assert.IsTrue(true); + Assert.AreEqual(1, 1); + Assert.IsNotNull("test"); + + // Non-Assert.Throws methods should not be analyzed + var action = new Action(() => + { + Console.WriteLine("First"); + Console.WriteLine("Second"); + }); + action(); + } + } + """; + + await VerifyCS.VerifyAnalyzerAsync(code); + } + + [TestMethod] + public async Task WhenUsingOtherAssertMethods_VB_NoDiagnostic() + { + string code = """ + Imports System + Imports Microsoft.VisualStudio.TestTools.UnitTesting + + + Public Class MyTestClass + + Public Sub MyTestMethod() + ' Other Assert methods should not be flagged even with multiple statements + Assert.IsTrue(true) + Assert.AreEqual(1, 1) + Assert.IsNotNull("test") + + ' Non-Assert.Throws methods should not be analyzed + Dim action As new Action(Sub() + Console.WriteLine("First") + Console.WriteLine("Second") + End Sub) + action() + End Sub + End Class + """; + + await VerifyVB.VerifyAnalyzerAsync(code); + } + + [TestMethod] + public async Task WhenAssertThrowsHasMessageParameter_CSharp_StillAnalyzes() + { + string code = """ + using System; + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + // Multiple statements with message parameter - should be flagged + [|Assert.Throws(() => + { + Console.WriteLine("First"); + Console.WriteLine("Second"); + }, "Custom message")|]; + + // Single statement with message parameter - should NOT be flagged + Assert.Throws(() => DoSomething(), "Custom message"); + } + + private static void DoSomething() => throw new Exception(); + } + """; + + await VerifyCS.VerifyAnalyzerAsync(code); + } + + [TestMethod] + public async Task WhenAssertThrowsHasMessageParameter_VB_StillAnalyzes() + { + string code = """ + Imports System + Imports Microsoft.VisualStudio.TestTools.UnitTesting + + + Public Class MyTestClass + + Public Sub MyTestMethod() + ' Multiple statements with message parameter - should be flagged + [|Assert.Throws(Of Exception)(Sub() + Console.WriteLine("First") + Console.WriteLine("Second") + End Sub, "Custom message")|] + + ' Single statement with message parameter - should NOT be flagged + Assert.Throws(Of Exception)(Sub() DoSomething(), "Custom message") + End Sub + + Private Shared Sub DoSomething() + Throw New Exception() + End Sub + End Class + """; + + await VerifyVB.VerifyAnalyzerAsync(code); + } + + [TestMethod] + public async Task WhenAssertThrowsWithExpressionBody_CSharp_NoDiagnostic() + { + string code = """ + using System; + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + // Expression-bodied lambda - should NOT be flagged + Assert.Throws(() => DoSomething()); + Assert.ThrowsExactly(() => DoSomething()); + + // Expression-bodied with method chain - should NOT be flagged + Assert.Throws(() => "test".ToUpper().ToLower()); + } + + private static void DoSomething() => throw new Exception(); + } + """; + + await VerifyCS.VerifyAnalyzerAsync(code); + } + + [TestMethod] + public async Task WhenAssertThrowsWithExpressionBody_VB_NoDiagnostic() + { + string code = """ + Imports System + Imports Microsoft.VisualStudio.TestTools.UnitTesting + + + Public Class MyTestClass + + Public Sub MyTestMethod() + ' Expression-bodied lambda - should NOT be flagged + Assert.Throws(Of Exception)(Sub() DoSomething()) + Assert.ThrowsExactly(Of Exception)(Sub() DoSomething()) + + ' Expression-bodied with method chain - should NOT be flagged + Assert.Throws(Of Exception)(Function() "test".ToUpper().ToLower()) + End Sub + + Private Shared Sub DoSomething() + Throw New Exception() + End Sub + End Class + """; + + await VerifyVB.VerifyAnalyzerAsync(code); + } + + [TestMethod] + public async Task WhenAssertThrowsWithEmptyStatements_CSharp_NoDiagnostic() + { + string code = """ + using System; + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + // Single statement with empty statements - should NOT be flagged + Assert.Throws(() => + { + DoSomething(); + ; // empty statement + }); + } + + private static void DoSomething() => throw new Exception(); + } + """; + + await VerifyCS.VerifyAnalyzerAsync(code); + } + + [TestMethod] + public async Task WhenAssertThrowsWithMultipleNonEmptyStatements_CSharp_Diagnostic() + { + string code = """ + using System; + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + // Multiple non-empty statements - should be flagged + [|Assert.Throws(() => + { + DoSomething(); + DoSomething(); + ; // empty statement + })|]; + } + + private static void DoSomething() => throw new Exception(); + } + """; + + await VerifyCS.VerifyAnalyzerAsync(code); + } + + [TestMethod] + public async Task WhenAssertThrowsContainsMultipleStatementsOnSameLine_CSharp_Diagnostic() + { + string code = """ + using System; + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + // Multiple statements on same line - should be flagged (shows we count statements, not lines) + [|Assert.Throws(() => { DoSomething(); DoSomething(); })|]; + + // Multiple statements on same line in block - should be flagged + [|Assert.Throws(() => + { + DoSomething(); DoSomething(); + })|]; + } + + private static void DoSomething() => throw new Exception(); + } + """; + + await VerifyCS.VerifyAnalyzerAsync(code); + } +} diff --git a/test/UnitTests/MSTest.Analyzers.UnitTests/AssertionArgsShouldBePassedInCorrectOrderAnalyzerTests.cs b/test/UnitTests/MSTest.Analyzers.UnitTests/AssertionArgsShouldBePassedInCorrectOrderAnalyzerTests.cs index ba37e0da2b..82640559d0 100644 --- a/test/UnitTests/MSTest.Analyzers.UnitTests/AssertionArgsShouldBePassedInCorrectOrderAnalyzerTests.cs +++ b/test/UnitTests/MSTest.Analyzers.UnitTests/AssertionArgsShouldBePassedInCorrectOrderAnalyzerTests.cs @@ -816,4 +816,185 @@ await VerifyCS.VerifyCodeFixAsync( code, code); } + + [TestMethod] + public async Task LiteralWithExpectedNamedProperty_ShouldNotFlag() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + public class TestObject + { + public string ExpectedValue { get; set; } = ""; + public int expectedCount { get; set; } = 0; + public bool _expectedFlag { get; set; } = false; + } + + [TestMethod] + public void Compliant() + { + var obj = new TestObject(); + + // These should NOT be flagged - comparing literals to properties with "expected" names is correct + Assert.AreEqual("value1", obj.ExpectedValue); + Assert.AreEqual(42, obj.expectedCount); + Assert.AreEqual(true, obj._expectedFlag); + Assert.AreEqual(obj._expectedFlag, obj._expectedFlag); + Assert.AreEqual(obj.ExpectedValue, obj._expectedFlag); + + Assert.AreNotEqual("value2", obj.ExpectedValue); + Assert.AreNotEqual(24, obj.expectedCount); + Assert.AreNotEqual(false, obj._expectedFlag); + + Assert.AreSame("value3", obj.ExpectedValue); + Assert.AreNotSame("value4", obj.ExpectedValue); + } + } + """; + + await VerifyCS.VerifyCodeFixAsync(code, code); + } + + [TestMethod] + public async Task ConstantWithExpectedNamedProperty_ShouldNotFlag() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + private const string EXPECTED_CONSTANT = "test"; + + public class TestObject + { + public string ExpectedValue { get; set; } = ""; + public string expectedResult { get; set; } = ""; + } + + [TestMethod] + public void Compliant() + { + var obj = new TestObject(); + + // These should NOT be flagged - comparing constants to properties with "expected" names is correct + Assert.AreEqual(EXPECTED_CONSTANT, obj.ExpectedValue); + Assert.AreNotEqual(EXPECTED_CONSTANT, obj.expectedResult); + } + } + """; + + await VerifyCS.VerifyCodeFixAsync(code, code); + } + + [TestMethod] + public async Task TypeOfExpressions() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + using System; + using System.IO; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void NonCompliant() + { + Type actualType = typeof(string); + Exception exception = new IOException(); + + // These should be flagged - typeof() expressions should be treated as constants + [|Assert.AreEqual(actualType, typeof(string))|]; + [|Assert.AreEqual(exception.GetType(), typeof(IOException))|]; + [|Assert.AreNotEqual(actualType, typeof(int))|]; + [|Assert.AreSame(actualType, typeof(string))|]; + [|Assert.AreNotSame(actualType, typeof(int))|]; + } + + [TestMethod] + public void Compliant() + { + Type actualType = typeof(string); + Exception exception = new IOException(); + + // These are correct - typeof() expressions as expected values + Assert.AreEqual(typeof(string), actualType); + Assert.AreEqual(typeof(IOException), exception.GetType()); + Assert.AreNotEqual(typeof(int), actualType); + Assert.AreSame(typeof(string), actualType); + Assert.AreNotSame(typeof(int), actualType); + } + } + """; + + string fixedCode = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + using System; + using System.IO; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void NonCompliant() + { + Type actualType = typeof(string); + Exception exception = new IOException(); + + // These should be flagged - typeof() expressions should be treated as constants + Assert.AreEqual(typeof(string), actualType); + Assert.AreEqual(typeof(IOException), exception.GetType()); + Assert.AreNotEqual(typeof(int), actualType); + Assert.AreSame(typeof(string), actualType); + Assert.AreNotSame(typeof(int), actualType); + } + + [TestMethod] + public void Compliant() + { + Type actualType = typeof(string); + Exception exception = new IOException(); + + // These are correct - typeof() expressions as expected values + Assert.AreEqual(typeof(string), actualType); + Assert.AreEqual(typeof(IOException), exception.GetType()); + Assert.AreNotEqual(typeof(int), actualType); + Assert.AreSame(typeof(string), actualType); + Assert.AreNotSame(typeof(int), actualType); + } + } + """; + + await VerifyCS.VerifyCodeFixAsync( + code, + fixedCode); + } + + [TestMethod] + public async Task TypeOfExpressionsAndConstants_ShouldNotFlag() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + using System; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void Compliant() + { + // When both are typeof() expressions, don't flag + Assert.AreEqual(typeof(string), typeof(string)); + Assert.AreEqual(typeof(int), typeof(int)); + Assert.AreNotEqual(typeof(string), typeof(int)); + } + } + """; + + await VerifyCS.VerifyCodeFixAsync(code, code); + } } diff --git a/test/UnitTests/MSTest.Analyzers.UnitTests/AvoidAssertFormatParametersAnalyzerTests.cs b/test/UnitTests/MSTest.Analyzers.UnitTests/AvoidAssertFormatParametersAnalyzerTests.cs new file mode 100644 index 0000000000..af3a29788e --- /dev/null +++ b/test/UnitTests/MSTest.Analyzers.UnitTests/AvoidAssertFormatParametersAnalyzerTests.cs @@ -0,0 +1,413 @@ +// 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.AvoidAssertFormatParametersAnalyzer, + MSTest.Analyzers.CodeFixes.AvoidAssertFormatParametersFixer>; + +namespace MSTest.Analyzers.UnitTests; + +[TestClass] +public sealed class AvoidAssertFormatParametersAnalyzerTests +{ + [TestMethod] + public async Task WhenAssertMethodWithoutFormatParameters_NoDiagnostic() + { + const string code = """ + using System; + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + Assert.IsTrue(true); + Assert.IsTrue(true, "Simple message"); + Assert.AreEqual(1, 2); + Assert.AreEqual(1, 2, "Simple message"); + Assert.Throws(() => { }); + Assert.Throws(() => { }, "message"); + } + } + """; + + await VerifyCS.VerifyAnalyzerAsync(code); + } + + [TestMethod] + public async Task WhenAssertIsTrueWithFormatParameters_Diagnostic() + { + const string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + [|Assert.IsTrue(true, "Value: {0}", 42)|]; + } + } + """; + + await VerifyCS.VerifyAnalyzerAsync(code); + } + + [TestMethod] + public async Task WhenAssertIsFalseWithFormatParameters_Diagnostic() + { + const string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + [|Assert.IsFalse(false, "Value: {0}", 42)|]; + } + } + """; + + await VerifyCS.VerifyAnalyzerAsync(code); + } + + [TestMethod] + public async Task WhenAssertAreEqualWithFormatParameters_Diagnostic() + { + const string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + [|Assert.AreEqual(1, 2, "Expected {0} but got {1}", 1, 2)|]; + } + } + """; + + await VerifyCS.VerifyAnalyzerAsync(code); + } + + [TestMethod] + public async Task WhenCollectionAssertWithFormatParameters_Diagnostic() + { + const string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + using System.Collections.Generic; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + var list1 = new List { 1, 2, 3 }; + var list2 = new List { 1, 2, 3 }; + [|CollectionAssert.AreEqual(list1, list2, "Collections differ: {0}", "details")|]; + } + } + """; + + await VerifyCS.VerifyAnalyzerAsync(code); + } + + [TestMethod] + public async Task WhenStringAssertWithFormatParameters_Diagnostic() + { + const string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + [|StringAssert.Contains("hello", "world", "String '{0}' not found in '{1}'", "hello", "world")|]; + } + } + """; + + await VerifyCS.VerifyAnalyzerAsync(code); + } + + [TestMethod] + public async Task WhenAssertWithFormatParameters_FixWithStringFormat() + { + const string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + [|Assert.IsTrue(true, "Value: {0}", 42)|]; + } + } + """; + + const string fixedCode = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + Assert.IsTrue(true, string.Format("Value: {0}", 42)); + } + } + """; + + await new VerifyCS.Test + { + CodeActionIndex = 0, // Use first code fix (string.Format) + TestCode = code, + FixedCode = fixedCode, + }.RunAsync(); + } + + [TestMethod] + public async Task WhenAssertWithFormatParameters_FixWithInterpolatedString() + { + const string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + [|Assert.IsTrue(true, "Value: {0}", 42)|]; + [|Assert.IsTrue(true, "Value: {0} {2} {1} {3}", 42, 44, 43, true ? "True" : "False")|]; + } + } + """; + + const string fixedCode = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + Assert.IsTrue(true, $"Value: {42}"); + Assert.IsTrue(true, $"Value: {42} {43} {44} {(true ? "True" : "False")}"); + } + } + """; + + await new VerifyCS.Test + { + CodeActionIndex = 1, // Use second code fix (interpolated string) + TestCode = code, + FixedCode = fixedCode, + }.RunAsync(); + } + + [TestMethod] + public async Task WhenAssertWithMultipleFormatParameters_FixWithStringFormat() + { + const string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + [|Assert.AreEqual(1, 2, "Expected {0} but got {1}", 1, 2)|]; + } + } + """; + + const string fixedCode = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + Assert.AreEqual(1, 2, string.Format("Expected {0} but got {1}", 1, 2)); + } + } + """; + + await new VerifyCS.Test + { + CodeActionIndex = 0, // Use first code fix (string.Format) + TestCode = code, + FixedCode = fixedCode, + }.RunAsync(); + } + + [TestMethod] + public async Task WhenAssertWithArrayFormatParameters_FixWithStringFormat() + { + const string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + [|Assert.AreEqual(1, 2, "Expected {0} but got {1}", new object[] { 1, 2 })|]; + } + } + """; + + const string fixedCode = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + Assert.AreEqual(1, 2, string.Format("Expected {0} but got {1}", new object[] { 1, 2 })); + } + } + """; + + await new VerifyCS.Test + { + CodeActionIndex = 0, // Use first code fix (string.Format) + TestCode = code, + FixedCode = fixedCode, + }.RunAsync(); + } + + [TestMethod] + public async Task WhenAssertWithMultipleFormatParameters_FixWithInterpolatedString() + { + const string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + [|Assert.AreEqual(1, 2, "Expected {0} but got {1}", 1, 2)|]; + } + } + """; + + const string fixedCode = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + Assert.AreEqual(1, 2, $"Expected {1} but got {2}"); + } + } + """; + + await new VerifyCS.Test + { + CodeActionIndex = 1, // Use second code fix (interpolated string) + TestCode = code, + FixedCode = fixedCode, + }.RunAsync(); + } + + [TestMethod] + public async Task WhenStringAssertWithFormatParameters_FixWithStringFormat() + { + const string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + [|StringAssert.Contains("hello", "world", "String '{0}' not found", "hello")|]; + } + } + """; + + const string fixedCode = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + StringAssert.Contains("hello", "world", string.Format("String '{0}' not found", "hello")); + } + } + """; + + await new VerifyCS.Test + { + CodeActionIndex = 0, // Use first code fix (string.Format) + TestCode = code, + FixedCode = fixedCode, + }.RunAsync(); + } + + [TestMethod] + public async Task WhenNonStringLiteralFormatString_OnlyStringFormatFix() + { + const string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + string format = "Value: {0}"; + [|Assert.IsTrue(true, format, 42)|]; + } + } + """; + + const string fixedCode = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + string format = "Value: {0}"; + Assert.IsTrue(true, string.Format(format, 42)); + } + } + """; + + await VerifyCS.VerifyCodeFixAsync(code, fixedCode); + } +} diff --git a/test/UnitTests/MSTest.Analyzers.UnitTests/AvoidExplicitDynamicDataSourceTypeTests.cs b/test/UnitTests/MSTest.Analyzers.UnitTests/AvoidExplicitDynamicDataSourceTypeTests.cs new file mode 100644 index 0000000000..3e109a390e --- /dev/null +++ b/test/UnitTests/MSTest.Analyzers.UnitTests/AvoidExplicitDynamicDataSourceTypeTests.cs @@ -0,0 +1,266 @@ +// 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.AvoidExplicitDynamicDataSourceTypeAnalyzer, + MSTest.Analyzers.AvoidExplicitDynamicDataSourceTypeFixer>; + +namespace MSTest.Analyzers.Test; + +[TestClass] +public sealed class AvoidExplicitDynamicDataSourceTypeTests +{ + [TestMethod] + public async Task WhenDynamicDataUsesAutoDetectImplicitly_NoDiagnostic() + { + string code = """ + using System.Collections.Generic; + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [DynamicData("Data")] + [TestMethod] + public void TestMethod1(object[] o) { } + + + [DynamicData("Data", typeof(MyTestClass))] + [TestMethod] + public void TestMethod2(object[] o) { } + + static IEnumerable Data => new[] { new object[] { 1 } }; + } + """; + + await VerifyCS.VerifyCodeFixAsync(code, code); + } + + [TestMethod] + public async Task WhenDynamicDataUsesAutoDetectExplicitly_Diagnostic() + { + string code = """ + using System.Collections.Generic; + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [[|DynamicData("Data", DynamicDataSourceType.AutoDetect)|]] + [TestMethod] + public void TestMethod1(object[] o) { } + + [[|DynamicData("Data", typeof(MyTestClass), DynamicDataSourceType.AutoDetect)|]] + [TestMethod] + public void TestMethod2(object[] o) { } + + static IEnumerable Data => new[] { new object[] { 1 } }; + } + """; + + string fixedCode = """ + using System.Collections.Generic; + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [DynamicData("Data")] + [TestMethod] + public void TestMethod1(object[] o) { } + + [DynamicData("Data", typeof(MyTestClass))] + [TestMethod] + public void TestMethod2(object[] o) { } + + static IEnumerable Data => new[] { new object[] { 1 } }; + } + """; + + await VerifyCS.VerifyCodeFixAsync(code, fixedCode); + } + + [TestMethod] + public async Task WhenDynamicDataUsesProperty_ReportsDiagnostic() + { + string code = """ + using System.Collections.Generic; + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [[|DynamicData("Data", DynamicDataSourceType.Property)|]] + [TestMethod] + public void TestMethod1(object[] o) { } + + static IEnumerable Data => new[] { new object[] { 1 } }; + } + """; + + string fixedCode = """ + using System.Collections.Generic; + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [DynamicData("Data")] + [TestMethod] + public void TestMethod1(object[] o) { } + + static IEnumerable Data => new[] { new object[] { 1 } }; + } + """; + + await VerifyCS.VerifyCodeFixAsync(code, fixedCode); + } + + [TestMethod] + public async Task WhenDynamicDataUsesMethod_ReportsDiagnostic() + { + string code = """ + using System.Collections.Generic; + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [[|DynamicData("GetData", DynamicDataSourceType.Method)|]] + [TestMethod] + public void TestMethod1(object[] o) { } + + static IEnumerable GetData() => new[] { new object[] { 1 } }; + } + """; + + string fixedCode = """ + using System.Collections.Generic; + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [DynamicData("GetData")] + [TestMethod] + public void TestMethod1(object[] o) { } + + static IEnumerable GetData() => new[] { new object[] { 1 } }; + } + """; + + await VerifyCS.VerifyCodeFixAsync(code, fixedCode); + } + + [TestMethod] + public async Task WhenDynamicDataUsesPropertyWithDeclaringType_ReportsDiagnostic() + { + string code = """ + using System.Collections.Generic; + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [[|DynamicData("Data", typeof(MyTestClass), DynamicDataSourceType.Property)|]] + [TestMethod] + public void TestMethod1(object[] o) { } + + static IEnumerable Data => new[] { new object[] { 1 } }; + } + """; + + string fixedCode = """ + using System.Collections.Generic; + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [DynamicData("Data", typeof(MyTestClass))] + [TestMethod] + public void TestMethod1(object[] o) { } + + static IEnumerable Data => new[] { new object[] { 1 } }; + } + """; + + await VerifyCS.VerifyCodeFixAsync(code, fixedCode); + } + + [TestMethod] + public async Task WhenDynamicDataUsesMethodWithDeclaringType_ReportsDiagnostic() + { + string code = """ + using System.Collections.Generic; + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [[|DynamicData("GetData", typeof(MyTestClass), DynamicDataSourceType.Method)|]] + [TestMethod] + public void TestMethod1(object[] o) { } + + static IEnumerable GetData() => new[] { new object[] { 1 } }; + } + """; + + string fixedCode = """ + using System.Collections.Generic; + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [DynamicData("GetData", typeof(MyTestClass))] + [TestMethod] + public void TestMethod1(object[] o) { } + + static IEnumerable GetData() => new[] { new object[] { 1 } }; + } + """; + + await VerifyCS.VerifyCodeFixAsync(code, fixedCode); + } + + [TestMethod] + public async Task WhenMultipleDynamicDataAttributesWithExplicitTypes_ReportsMultipleDiagnostics() + { + string code = """ + using System.Collections.Generic; + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [[|DynamicData("Data", DynamicDataSourceType.Property)|]] + [[|DynamicData("GetData", DynamicDataSourceType.Method)|]] + [TestMethod] + public void TestMethod1(object[] o) { } + + static IEnumerable Data => new[] { new object[] { 1 } }; + static IEnumerable GetData() => new[] { new object[] { 2 } }; + } + """; + + string fixedCode = """ + using System.Collections.Generic; + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [DynamicData("Data")] + [DynamicData("GetData")] + [TestMethod] + public void TestMethod1(object[] o) { } + + static IEnumerable Data => new[] { new object[] { 1 } }; + static IEnumerable GetData() => new[] { new object[] { 2 } }; + } + """; + + await VerifyCS.VerifyCodeFixAsync(code, fixedCode); + } +} diff --git a/test/UnitTests/MSTest.Analyzers.UnitTests/AvoidUsingAssertsInAsyncVoidContextAnalyzerTests.cs b/test/UnitTests/MSTest.Analyzers.UnitTests/AvoidUsingAssertsInAsyncVoidContextAnalyzerTests.cs index 378eb183f4..d30fa650da 100644 --- a/test/UnitTests/MSTest.Analyzers.UnitTests/AvoidUsingAssertsInAsyncVoidContextAnalyzerTests.cs +++ b/test/UnitTests/MSTest.Analyzers.UnitTests/AvoidUsingAssertsInAsyncVoidContextAnalyzerTests.cs @@ -131,4 +131,102 @@ async void d() await VerifyCS.VerifyCodeFixAsync(code, code); } + + [TestMethod] + public async Task UseStringAssertMethodInAsyncVoidMethod_Diagnostic() + { + string code = """ + using System.Threading.Tasks; + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public async void TestMethod() + { + await Task.Delay(1); + [|StringAssert.Contains("abc", "a")|]; + } + } + """; + + await VerifyCS.VerifyCodeFixAsync(code, code); + } + + [TestMethod] + public async Task UseCollectionAssertMethodInAsyncVoidMethod_Diagnostic() + { + string code = """ + using System.Collections; + using System.Threading.Tasks; + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public async void TestMethod() + { + await Task.Delay(1); + [|CollectionAssert.AreEqual(new[] { 1 }, new[] { 1 })|]; + } + } + """; + + await VerifyCS.VerifyCodeFixAsync(code, code); + } + + [TestMethod] + public async Task UseStringAssertMethodInAsyncVoidLocalFunction_Diagnostic() + { + string code = """ + using System.Threading.Tasks; + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void TestMethod() + { + async void d() + { + await Task.Delay(1); + [|StringAssert.Contains("abc", "a")|]; + }; + } + } + """; + + await VerifyCS.VerifyCodeFixAsync(code, code); + } + + [TestMethod] + public async Task UseCollectionAssertMethodInAsyncVoidDelegate_Diagnostic() + { + string code = """ + using System.Collections; + using System.Threading.Tasks; + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + internal delegate void MyDelegate(); + + [TestMethod] + public void TestMethod() + { + MyDelegate d = async () => + { + await Task.Delay(1); + [|CollectionAssert.AreEqual(new[] { 1 }, new[] { 1 })|]; + }; + } + } + """; + + await VerifyCS.VerifyCodeFixAsync(code, code); + } } diff --git a/test/UnitTests/MSTest.Analyzers.UnitTests/ClassCleanupShouldBeValidAnalyzerTests.cs b/test/UnitTests/MSTest.Analyzers.UnitTests/ClassCleanupShouldBeValidAnalyzerTests.cs index bf5c6d78d3..a6693c6028 100644 --- a/test/UnitTests/MSTest.Analyzers.UnitTests/ClassCleanupShouldBeValidAnalyzerTests.cs +++ b/test/UnitTests/MSTest.Analyzers.UnitTests/ClassCleanupShouldBeValidAnalyzerTests.cs @@ -302,6 +302,7 @@ public static void ClassCleanup(TestContext testContext) await VerifyCS.VerifyCodeFixAsync(code, code); } +#if NET [TestMethod] public async Task WhenClassCleanupReturnTypeIsNotValid_Diagnostic() { @@ -409,6 +410,7 @@ public static ValueTask ClassCleanup2() await VerifyCS.VerifyAnalyzerAsync(code); } +#endif [TestMethod] public async Task WhenClassCleanupIsAsyncVoid_Diagnostic() diff --git a/test/UnitTests/MSTest.Analyzers.UnitTests/ClassInitializeShouldBeValidAnalyzerTests.cs b/test/UnitTests/MSTest.Analyzers.UnitTests/ClassInitializeShouldBeValidAnalyzerTests.cs index 55cc137e6f..9e279b7ac4 100644 --- a/test/UnitTests/MSTest.Analyzers.UnitTests/ClassInitializeShouldBeValidAnalyzerTests.cs +++ b/test/UnitTests/MSTest.Analyzers.UnitTests/ClassInitializeShouldBeValidAnalyzerTests.cs @@ -320,6 +320,7 @@ await VerifyCS.VerifyCodeFixAsync( fixedCode); } +#if NET [TestMethod] public async Task WhenClassInitializeReturnTypeIsNotValid_Diagnostic() { @@ -427,6 +428,7 @@ public static ValueTask ClassInitialize2(TestContext testContext) await VerifyCS.VerifyAnalyzerAsync(code); } +#endif [TestMethod] public async Task WhenClassInitializeIsAsyncVoid_Diagnostic() diff --git a/test/UnitTests/MSTest.Analyzers.UnitTests/DataRowShouldBeValidAnalyzerTests.cs b/test/UnitTests/MSTest.Analyzers.UnitTests/DataRowShouldBeValidAnalyzerTests.cs index a355034f92..e7e334d27f 100644 --- a/test/UnitTests/MSTest.Analyzers.UnitTests/DataRowShouldBeValidAnalyzerTests.cs +++ b/test/UnitTests/MSTest.Analyzers.UnitTests/DataRowShouldBeValidAnalyzerTests.cs @@ -155,7 +155,7 @@ await VerifyCS.VerifyAnalyzerAsync( code, VerifyCS.Diagnostic(DataRowShouldBeValidAnalyzer.ArgumentTypeMismatchRule) .WithLocation(0) - .WithArguments((0, 0))); + .WithArguments("Parameter 'o' expects type 'object[]', but the provided value has type 'int'")); } [TestMethod] @@ -490,7 +490,57 @@ await VerifyCS.VerifyAnalyzerAsync( code, VerifyCS.Diagnostic(DataRowShouldBeValidAnalyzer.ArgumentTypeMismatchRule) .WithLocation(0) - .WithArguments((2, 2))); + .WithArguments("Parameter 's' expects type 'string', but the provided value has type 'int'")); + } + + [TestMethod] + public async Task WhenDataRowHasDecimalDoubleTypeMismatch_Diagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [{|#0:DataRow("Luxury Car", "Alice Johnson", 1500.00, "https://example.com/luxurycar.jpg", "https://example.com/luxurycar")|}] + [TestMethod] + public void TestMethod1(string name, string reservedBy, decimal price, string imageUrl, string link) + { + } + } + """ + ; + + await VerifyCS.VerifyAnalyzerAsync( + code, + VerifyCS.Diagnostic(DataRowShouldBeValidAnalyzer.ArgumentTypeMismatchRule) + .WithLocation(0) + .WithArguments("Parameter 'price' expects type 'decimal', but the provided value has type 'double'")); + } + + [TestMethod] + public async Task WhenDataRowHasMultipleTypeMismatches_SingleDiagnosticWithAllMismatches() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [{|#0:DataRow(1, 2, 3)|}] + [TestMethod] + public void TestMethod1(string s, decimal d, bool b) + { + } + } + """ + ; + + await VerifyCS.VerifyAnalyzerAsync( + code, + VerifyCS.Diagnostic(DataRowShouldBeValidAnalyzer.ArgumentTypeMismatchRule) + .WithLocation(0) + .WithArguments("Parameter 's' expects type 'string', but the provided value has type 'int'; Parameter 'b' expects type 'bool', but the provided value has type 'int'")); } [TestMethod] diff --git a/test/UnitTests/MSTest.Analyzers.UnitTests/DoNotStoreStaticTestContextAnalyzerTests.cs b/test/UnitTests/MSTest.Analyzers.UnitTests/DoNotStoreStaticTestContextAnalyzerTests.cs index f0c3590176..0ad128b738 100644 --- a/test/UnitTests/MSTest.Analyzers.UnitTests/DoNotStoreStaticTestContextAnalyzerTests.cs +++ b/test/UnitTests/MSTest.Analyzers.UnitTests/DoNotStoreStaticTestContextAnalyzerTests.cs @@ -10,6 +10,7 @@ namespace MSTest.Analyzers.Test; [TestClass] public sealed class DoNotStoreStaticTestContextAnalyzerTests { +#if NET [TestMethod] public async Task WhenAssemblyInitializeOrClassInitialize_Diagnostic() { @@ -87,6 +88,7 @@ public static ValueTask ClassInit(TestContext tc) await VerifyCS.VerifyAnalyzerAsync(code); } +#endif [TestMethod] public async Task WhenOtherTestContext_NoDiagnostic() diff --git a/test/UnitTests/MSTest.Analyzers.UnitTests/DuplicateDataRowAnalyzerTests.cs b/test/UnitTests/MSTest.Analyzers.UnitTests/DuplicateDataRowAnalyzerTests.cs index 52a788fcb2..49c9de6b33 100644 --- a/test/UnitTests/MSTest.Analyzers.UnitTests/DuplicateDataRowAnalyzerTests.cs +++ b/test/UnitTests/MSTest.Analyzers.UnitTests/DuplicateDataRowAnalyzerTests.cs @@ -177,4 +177,116 @@ public static void TestMethod() await VerifyCS.VerifyAnalyzerAsync(code); } + + [TestMethod] + public async Task WhenZeroAndNegativeZero_NoDiagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + [DataRow(0.0d)] + [DataRow(-0.0d)] + public static void TestMethod1(double x) + { + } + + [TestMethod] + [DataRow(0.0f)] + [DataRow(-0.0f)] + public static void TestMethod2(float x) + { + } + } + """; + + await VerifyCS.VerifyAnalyzerAsync(code); + } + + [TestMethod] + public async Task WhenZeroIsDuplicated_Diagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + [DataRow(0.0d)] + [[|DataRow(0d)|]] + public static void TestMethod1(double x) + { + } + + [TestMethod] + [DataRow(0.0d)] + [[|DataRow(0.0d)|]] + public static void TestMethod2(double x) + { + } + + [TestMethod] + [DataRow(0.0f)] + [[|DataRow(0f)|]] + public static void TestMethod3(float x) + { + } + + [TestMethod] + [DataRow(0.0f)] + [[|DataRow(0.0f)|]] + public static void TestMethod4(float x) + { + } + } + """; + + await VerifyCS.VerifyAnalyzerAsync(code); + } + + [TestMethod] + public async Task WhenNegativeZeroIsDuplicated_Diagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + [DataRow(-0.0d)] + [[|DataRow(-0d)|]] + public static void TestMethod1(double x) + { + } + + [TestMethod] + [DataRow(-0.0d)] + [[|DataRow(-0.0d)|]] + public static void TestMethod2(double x) + { + } + + [TestMethod] + [DataRow(-0.0f)] + [[|DataRow(-0f)|]] + public static void TestMethod3(float x) + { + } + + [TestMethod] + [DataRow(-0.0f)] + [[|DataRow(-0.0f)|]] + public static void TestMethod4(float x) + { + } + } + """; + + await VerifyCS.VerifyAnalyzerAsync(code); + } } diff --git a/test/UnitTests/MSTest.Analyzers.UnitTests/DynamicDataShouldBeValidAnalyzerTests.cs b/test/UnitTests/MSTest.Analyzers.UnitTests/DynamicDataShouldBeValidAnalyzerTests.cs index 4d8a1b3a03..f4f236ae00 100644 --- a/test/UnitTests/MSTest.Analyzers.UnitTests/DynamicDataShouldBeValidAnalyzerTests.cs +++ b/test/UnitTests/MSTest.Analyzers.UnitTests/DynamicDataShouldBeValidAnalyzerTests.cs @@ -202,6 +202,7 @@ public class SomeClass await VerifyCS.VerifyAnalyzerAsync(code); } +#if NET [TestMethod] public async Task WhenDataIsIEnumerableValueTuple_NoDiagnostic() { @@ -273,6 +274,7 @@ public class SomeClass """; await VerifyCS.VerifyAnalyzerAsync(code); } +#endif [TestMethod] public async Task WhenDataIsJaggedArrays_NoDiagnostic() @@ -906,18 +908,6 @@ public void TestMethod16(object[] o) { } - [{|#10:DynamicData(nameof(DataField), DynamicDataSourceType.AutoDetect)|}] - [TestMethod] - public void TestMethod17(object[] o) - { - } - - [{|#11:DynamicData(nameof(DataField))|}] - [TestMethod] - public void TestMethod18(object[] o) - { - } - public static IEnumerable Data => new List(); public static IEnumerable GetData() => new List(); public static IEnumerable DataField = new List(); @@ -940,10 +930,142 @@ await VerifyCS.VerifyAnalyzerAsync( VerifyCS.Diagnostic(DynamicDataShouldBeValidAnalyzer.SourceTypePropertyRule).WithLocation(5).WithArguments("SomeClass", "SomeData"), VerifyCS.Diagnostic(DynamicDataShouldBeValidAnalyzer.SourceTypePropertyRule).WithLocation(6).WithArguments("MyTestClass", "Data"), VerifyCS.Diagnostic(DynamicDataShouldBeValidAnalyzer.SourceTypePropertyRule).WithLocation(7).WithArguments("SomeClass", "SomeData"), - VerifyCS.Diagnostic(DynamicDataShouldBeValidAnalyzer.SourceTypeNotPropertyOrMethodRule).WithLocation(8).WithArguments("MyTestClass", "DataField"), - VerifyCS.Diagnostic(DynamicDataShouldBeValidAnalyzer.SourceTypeNotPropertyOrMethodRule).WithLocation(9).WithArguments("MyTestClass", "DataField"), - VerifyCS.Diagnostic(DynamicDataShouldBeValidAnalyzer.SourceTypeNotPropertyOrMethodRule).WithLocation(10).WithArguments("MyTestClass", "DataField"), - VerifyCS.Diagnostic(DynamicDataShouldBeValidAnalyzer.SourceTypeNotPropertyOrMethodRule).WithLocation(11).WithArguments("MyTestClass", "DataField")); + VerifyCS.Diagnostic(DynamicDataShouldBeValidAnalyzer.SourceTypeFieldRule).WithLocation(8).WithArguments("MyTestClass", "DataField"), + VerifyCS.Diagnostic(DynamicDataShouldBeValidAnalyzer.SourceTypeFieldRule).WithLocation(9).WithArguments("MyTestClass", "DataField")); + } + + [TestMethod] + public async Task WhenDataIsField_NoDiagnostic() + { + string code = """ + using System.Collections.Generic; + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [DynamicData("DataField")] + [TestMethod] + public void TestMethod1Auto(object[] o) + { + } + + [DynamicData("DataField", DynamicDataSourceType.Field)] + [TestMethod] + public void TestMethod1Field(object[] o) + { + } + + [DynamicData("SomeDataField", typeof(SomeClass))] + [TestMethod] + public void TestMethod2Auto(object[] o) + { + } + + [DynamicData("SomeDataField", typeof(SomeClass), DynamicDataSourceType.Field)] + [TestMethod] + public void TestMethod2Field(object[] o) + { + } + + [DynamicData(dynamicDataSourceName: "DataField")] + [TestMethod] + public void TestMethod3Auto(object[] o) + { + } + + [DynamicData(dynamicDataDeclaringType: typeof(SomeClass), dynamicDataSourceName: "SomeDataField")] + [TestMethod] + public void TestMethod4Auto(object[] o) + { + } + + [DynamicData(dynamicDataDeclaringType: typeof(SomeClass), dynamicDataSourceName: "SomeDataField", dynamicDataSourceType: DynamicDataSourceType.Field)] + [TestMethod] + public void TestMethod4Field(object[] o) + { + } + + public static IEnumerable DataField = new[] + { + new object[] { 1, 2 }, + new object[] { 3, 4 } + }; + } + + public class SomeClass + { + public static IEnumerable SomeDataField = new[] + { + new object[] { 1, 2 }, + new object[] { 3, 4 } + }; + } + """; + + await VerifyCS.VerifyAnalyzerAsync(code); + } + + [TestMethod] + public async Task WhenFieldMemberKindIsMixedUp_Diagnostic() + { + string code = """ + using System.Collections.Generic; + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [{|#0:DynamicData("DataField", DynamicDataSourceType.Property)|}] + [TestMethod] + public void TestMethod1(object[] o) + { + } + + [{|#1:DynamicData("DataField", DynamicDataSourceType.Method)|}] + [TestMethod] + public void TestMethod2(object[] o) + { + } + + [{|#2:DynamicData("Data", DynamicDataSourceType.Field)|}] + [TestMethod] + public void TestMethod3(object[] o) + { + } + + [{|#3:DynamicData("GetData", DynamicDataSourceType.Field)|}] + [TestMethod] + public void TestMethod4(object[] o) + { + } + + public static IEnumerable Data => new[] + { + new object[] { 1, 2 }, + new object[] { 3, 4 } + }; + + public static IEnumerable GetData() => new[] + { + new object[] { 1, 2 }, + new object[] { 3, 4 } + }; + + public static IEnumerable DataField = new[] + { + new object[] { 1, 2 }, + new object[] { 3, 4 } + }; + } + """; + + await VerifyCS.VerifyAnalyzerAsync( + code, + VerifyCS.Diagnostic(DynamicDataShouldBeValidAnalyzer.SourceTypeFieldRule).WithLocation(0).WithArguments("MyTestClass", "DataField"), + VerifyCS.Diagnostic(DynamicDataShouldBeValidAnalyzer.SourceTypeFieldRule).WithLocation(1).WithArguments("MyTestClass", "DataField"), + VerifyCS.Diagnostic(DynamicDataShouldBeValidAnalyzer.SourceTypePropertyRule).WithLocation(2).WithArguments("MyTestClass", "Data"), + VerifyCS.Diagnostic(DynamicDataShouldBeValidAnalyzer.SourceTypeMethodRule).WithLocation(3).WithArguments("MyTestClass", "GetData")); } [TestMethod] @@ -1641,4 +1763,56 @@ public void TestMethod2(char c) """, VerifyCS.Diagnostic(DynamicDataShouldBeValidAnalyzer.MemberTypeRule).WithLocation(0).WithArguments("MyTestClass", "GetData")); + + [TestMethod] + public async Task WhenDataWithArgument_NoDiagnostic() + => await VerifyCS.VerifyAnalyzerAsync( + """ + using System; + using System.Collections.Generic; + using Microsoft.VisualStudio.TestTools.UnitTesting; + [TestClass] + public class MyTestClass + { + [TestMethod] + [DynamicData(nameof(GetData), 4)] + public void TestMethod(int a) + => Assert.IsInRange(4, 6, a); + + public static IEnumerable GetData(int i) + { + yield return i++; + yield return i++; + yield return i++; + } + } + + """); + + [TestMethod] + public async Task WhenDataWithArgument_ParameterCountMismatch_Diagnostic() + => await VerifyCS.VerifyAnalyzerAsync( + """ + using System; + using System.Collections.Generic; + using Microsoft.VisualStudio.TestTools.UnitTesting; + [TestClass] + public class MyTestClass + { + [TestMethod] + [{|#0:DynamicData(nameof(GetData), 4, 5)|}] + public void TestMethod(int a) + { + } + + public static IEnumerable GetData(int i) + { + yield return i++; + yield return i++; + yield return i++; + } + } + + """, + VerifyCS.Diagnostic(DynamicDataShouldBeValidAnalyzer.DataMemberSignatureRule).WithLocation(0).WithArguments("MyTestClass", "GetData")); } diff --git a/test/UnitTests/MSTest.Analyzers.UnitTests/FlowTestContextCancellationTokenAnalyzerTests.cs b/test/UnitTests/MSTest.Analyzers.UnitTests/FlowTestContextCancellationTokenAnalyzerTests.cs new file mode 100644 index 0000000000..81b2287f63 --- /dev/null +++ b/test/UnitTests/MSTest.Analyzers.UnitTests/FlowTestContextCancellationTokenAnalyzerTests.cs @@ -0,0 +1,789 @@ +// 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.FlowTestContextCancellationTokenAnalyzer, + MSTest.Analyzers.FlowTestContextCancellationTokenFixer>; + +namespace MSTest.Analyzers.Test; + +[TestClass] +public sealed class FlowTestContextCancellationTokenAnalyzerTests +{ + [TestMethod] + public async Task WhenTaskDelayWithoutCancellationToken_Diagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + using System.Threading; + using System.Threading.Tasks; + + [TestClass] + public class MyTestClass + { + public TestContext TestContext { get; set; } + + [TestMethod] + public async Task MyTestMethod() + { + await [|Task.Delay(1000)|]; + } + } + """; + + string fixedCode = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + using System.Threading; + using System.Threading.Tasks; + + [TestClass] + public class MyTestClass + { + public TestContext TestContext { get; set; } + + [TestMethod] + public async Task MyTestMethod() + { + await Task.Delay(1000, TestContext.CancellationToken); + } + } + """; + + await VerifyCS.VerifyCodeFixAsync(code, fixedCode); + } + + [TestMethod] + public async Task WhenMethodCallAlreadyHasCancellationToken_NoDiagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + using System.Threading; + using System.Threading.Tasks; + + [TestClass] + public class MyTestClass + { + public TestContext TestContext { get; set; } + + [TestMethod] + public async Task MyTestMethod() + { + await Task.Delay(1000, TestContext.CancellationToken); + } + } + """; + + await VerifyCS.VerifyCodeFixAsync(code, code); + } + + [TestMethod] + public async Task WhenNotInTestMethod_NoDiagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + using System.Threading; + using System.Threading.Tasks; + + public class MyClass + { + public async Task MyMethod() + { + await Task.Delay(1000); + } + } + """; + + await VerifyCS.VerifyCodeFixAsync(code, code); + } + + [TestMethod] + public async Task WhenMethodHasNoOverloadWithCancellationToken_NoDiagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + using System; + using System.Threading; + using System.Threading.Tasks; + + [TestClass] + public class MyTestClass + { + public TestContext TestContext { get; set; } + + [TestMethod] + public void MyTestMethod() + { + Console.WriteLine("Hello World"); + } + } + """; + + await VerifyCS.VerifyCodeFixAsync(code, code); + } + + [TestMethod] + public async Task WhenInTestInitialize_Diagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + using System.Threading; + using System.Threading.Tasks; + + [TestClass] + public class MyTestClass + { + public TestContext TestContext { get; set; } + + [TestInitialize] + public async Task TestInit() + { + await [|Task.Delay(1000)|]; + } + } + """; + + string fixedCode = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + using System.Threading; + using System.Threading.Tasks; + + [TestClass] + public class MyTestClass + { + public TestContext TestContext { get; set; } + + [TestInitialize] + public async Task TestInit() + { + await Task.Delay(1000, TestContext.CancellationToken); + } + } + """; + + await VerifyCS.VerifyCodeFixAsync(code, fixedCode); + } + + [TestMethod] + public async Task WhenInTestCleanup_Diagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + using System.Threading; + using System.Threading.Tasks; + + [TestClass] + public class MyTestClass + { + public TestContext TestContext { get; set; } + + [TestCleanup] + public async Task TestCleanup() + { + await [|Task.Delay(1000)|]; + } + } + """; + + string fixedCode = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + using System.Threading; + using System.Threading.Tasks; + + [TestClass] + public class MyTestClass + { + public TestContext TestContext { get; set; } + + [TestCleanup] + public async Task TestCleanup() + { + await Task.Delay(1000, TestContext.CancellationToken); + } + } + """; + + await VerifyCS.VerifyCodeFixAsync(code, fixedCode); + } + + [TestMethod] + public async Task WhenInClassInitialize_Diagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + using System.Threading; + using System.Threading.Tasks; + + [TestClass] + public class MyTestClass + { + [ClassInitialize] + public static async Task ClassInit(TestContext testContext) + { + await [|Task.Delay(1000)|]; + } + } + """; + + string fixedCode = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + using System.Threading; + using System.Threading.Tasks; + + [TestClass] + public class MyTestClass + { + [ClassInitialize] + public static async Task ClassInit(TestContext testContext) + { + await Task.Delay(1000, testContext.CancellationToken); + } + } + """; + + await VerifyCS.VerifyCodeFixAsync(code, fixedCode); + } + + [TestMethod] + public async Task WhenInClassCleanupWithTestContextParameter_Diagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + using System.Threading; + using System.Threading.Tasks; + + [TestClass] + public class MyTestClass + { + [ClassCleanup] + public static async Task ClassCleanup(TestContext testContext) + { + await [|Task.Delay(1000)|]; + } + } + """; + + string fixedCode = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + using System.Threading; + using System.Threading.Tasks; + + [TestClass] + public class MyTestClass + { + [ClassCleanup] + public static async Task ClassCleanup(TestContext testContext) + { + await Task.Delay(1000, testContext.CancellationToken); + } + } + """; + + await VerifyCS.VerifyCodeFixAsync(code, fixedCode); + } + + [TestMethod] + public async Task WhenInClassCleanupWithoutTestContextParameter_Diagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + using System.Threading; + using System.Threading.Tasks; + + [TestClass] + public class MyTestClass + { + [ClassCleanup] + public static async Task ClassCleanup() + { + await [|Task.Delay(1000)|]; + await [|Task.Delay(1000)|]; + } + } + """; + + string fixedCode = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + using System.Threading; + using System.Threading.Tasks; + + [TestClass] + public class MyTestClass + { + [ClassCleanup] + public static async Task ClassCleanup(TestContext testContext) + { + await Task.Delay(1000, testContext.CancellationToken); + await Task.Delay(1000, testContext.CancellationToken); + } + } + """; + + await VerifyCS.VerifyCodeFixAsync(code, fixedCode); + } + + [TestMethod] + public async Task WhenInTestMethodWithoutTestContextInScope_Diagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + using System.Threading; + using System.Threading.Tasks; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public async Task Test1() + { + await [|Task.Delay(1000)|]; + } + + [TestMethod] + [DataRow(0)] + [DataRow(1)] + public async Task Test2(int _) + { + await [|Task.Delay(1000)|]; + await [|Task.Delay(1000)|]; + } + } + """; + + string fixedCode = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + using System.Threading; + using System.Threading.Tasks; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public async Task Test1() + { + await Task.Delay(1000, TestContext.CancellationToken); + } + + [TestMethod] + [DataRow(0)] + [DataRow(1)] + public async Task Test2(int _) + { + await Task.Delay(1000, TestContext.CancellationToken); + await Task.Delay(1000, TestContext.CancellationToken); + } + + public TestContext TestContext { get; set; } + } + """; + + await VerifyCS.VerifyCodeFixAsync(code, fixedCode); + } + + [TestMethod] + public async Task WhenInTestMethodWithTestContextFieldInScope_Diagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + using System.Threading; + using System.Threading.Tasks; + + [TestClass] + public class MyTestClass + { + private readonly TestContext _testContext; + + public MyTestClass(TestContext testContext) + => _testContext = testContext; + + [TestMethod] + public async Task Test1() + { + await [|Task.Delay(1000)|]; + } + } + """; + + string fixedCode = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + using System.Threading; + using System.Threading.Tasks; + + [TestClass] + public class MyTestClass + { + private readonly TestContext _testContext; + + public MyTestClass(TestContext testContext) + => _testContext = testContext; + + [TestMethod] + public async Task Test1() + { + await Task.Delay(1000, _testContext.CancellationToken); + } + } + """; + + await VerifyCS.VerifyCodeFixAsync(code, fixedCode); + } + + [TestMethod] + public async Task WhenInTestMethodWithTestContextInScopeViaPrimaryConstructor_Diagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + using System.Threading; + using System.Threading.Tasks; + + [TestClass] + public class MyTestClass(TestContext MyTestContext) + { + [TestMethod] + public async Task Test1() + { + _ = MyTestContext; + await [|Task.Delay(1000)|]; + } + } + """; + + string fixedCode = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + using System.Threading; + using System.Threading.Tasks; + + [TestClass] + public class MyTestClass(TestContext MyTestContext) + { + [TestMethod] + public async Task Test1() + { + _ = MyTestContext; + await Task.Delay(1000, MyTestContext.CancellationToken); + } + } + """; + + await VerifyCS.VerifyCodeFixAsync(code, fixedCode); + } + + [TestMethod] + public async Task WhenWithCancellationTokenNone_NoDiagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + using System.Threading; + using System.Threading.Tasks; + + [TestClass] + public class MyTestClass + { + public TestContext TestContext { get; set; } + + [TestMethod] + public async Task MyTestMethod() + { + await Task.Delay(1000, CancellationToken.None); + } + } + """; + + await VerifyCS.VerifyCodeFixAsync(code, code); + } + + [TestMethod] + public async Task WhenHttpClientMethodWithoutCancellationToken_Diagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + using System.Net.Http; + using System.Threading; + using System.Threading.Tasks; + + [TestClass] + public class MyTestClass + { + public TestContext TestContext { get; set; } + + [TestMethod] + public async Task MyTestMethod() + { + using var client = new HttpClient(); + var response = await [|client.GetAsync("https://example.com")|]; + } + } + """; + + string fixedCode = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + using System.Net.Http; + using System.Threading; + using System.Threading.Tasks; + + [TestClass] + public class MyTestClass + { + public TestContext TestContext { get; set; } + + [TestMethod] + public async Task MyTestMethod() + { + using var client = new HttpClient(); + var response = await client.GetAsync("https://example.com", TestContext.CancellationToken); + } + } + """; + + await VerifyCS.VerifyCodeFixAsync(code, fixedCode); + } + + [TestMethod] + public async Task WhenMultipleFixesInSameClassWithoutTestContext_ShouldAddPropertyOnce() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + using System.Threading; + using System.Threading.Tasks; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public async Task Test1() + { + await [|Task.Delay(1000)|]; + await [|Task.Delay(2000)|]; + } + + [TestMethod] + public async Task Test2() + { + await [|Task.Delay(3000)|]; + } + } + """; + + string fixedCode = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + using System.Threading; + using System.Threading.Tasks; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public async Task Test1() + { + await Task.Delay(1000, TestContext.CancellationToken); + await Task.Delay(2000, TestContext.CancellationToken); + } + + [TestMethod] + public async Task Test2() + { + await Task.Delay(3000, TestContext.CancellationToken); + } + + public TestContext TestContext { get; set; } + } + """; + + await VerifyCS.VerifyCodeFixAsync(code, fixedCode); + } + + [TestMethod] + public async Task WhenMultipleFixesInSameClassMultiplePartialsWithoutTestContext_ShouldAddPropertyOnce() + { + var test = new VerifyCS.Test + { + TestState = + { + Sources = + { + """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + using System.Threading; + using System.Threading.Tasks; + + [TestClass] + public partial class MyTestClass + { + [TestMethod] + public async Task Test1() + { + await [|Task.Delay(1000)|]; + await [|Task.Delay(2000)|]; + } + + [TestMethod] + public async Task Test2() + { + await [|Task.Delay(3000)|]; + } + } + """, + """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + using System.Threading; + using System.Threading.Tasks; + + public partial class MyTestClass + { + [TestMethod] + public async Task Test3() + { + await [|Task.Delay(1000)|]; + await [|Task.Delay(2000)|]; + } + + [TestMethod] + public async Task Test4() + { + await [|Task.Delay(3000)|]; + } + } + """, + }, + }, + FixedState = + { + Sources = + { + """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + using System.Threading; + using System.Threading.Tasks; + + [TestClass] + public partial class MyTestClass + { + [TestMethod] + public async Task Test1() + { + await Task.Delay(1000, TestContext.CancellationToken); + await Task.Delay(2000, TestContext.CancellationToken); + } + + [TestMethod] + public async Task Test2() + { + await Task.Delay(3000, TestContext.CancellationToken); + } + + public TestContext TestContext { get; set; } + } + """, + """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + using System.Threading; + using System.Threading.Tasks; + + public partial class MyTestClass + { + [TestMethod] + public async Task Test3() + { + await Task.Delay(1000, TestContext.CancellationToken); + await Task.Delay(2000, TestContext.CancellationToken); + } + + [TestMethod] + public async Task Test4() + { + await Task.Delay(3000, TestContext.CancellationToken); + } + } + """, + }, + }, + }; + + await test.RunAsync(); + } + + [TestMethod] + public async Task WhenInAssemblyCleanupWithoutTestContextParameter_Diagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + using System.Threading; + using System.Threading.Tasks; + + [TestClass] + public class MyTestClass + { + [AssemblyCleanup] + public static async Task AssemblyCleanup() + { + await [|Task.Delay(1000)|]; + } + } + """; + + string fixedCode = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + using System.Threading; + using System.Threading.Tasks; + + [TestClass] + public class MyTestClass + { + [AssemblyCleanup] + public static async Task AssemblyCleanup(TestContext testContext) + { + await Task.Delay(1000, testContext.CancellationToken); + } + } + """; + + await VerifyCS.VerifyCodeFixAsync(code, fixedCode); + } + + [TestMethod] + public async Task WhenMethodWithPositionalArguments_ShouldUseNamedArgumentForCancellationToken() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + using System.Threading; + using System.Threading.Tasks; + public static class TestHelper + { + public static void DoSomething(string x = "", int i = 1, CancellationToken ct = default) { } + } + [TestClass] + public class MyTestClass + { + public TestContext TestContext { get; set; } + [TestMethod] + public void Test() + { + [|TestHelper.DoSomething("test")|]; + [|TestHelper.DoSomething("test", 42)|]; + } + } + """; + + string fixedCode = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + using System.Threading; + using System.Threading.Tasks; + public static class TestHelper + { + public static void DoSomething(string x = "", int i = 1, CancellationToken ct = default) { } + } + [TestClass] + public class MyTestClass + { + public TestContext TestContext { get; set; } + [TestMethod] + public void Test() + { + TestHelper.DoSomething("test", ct: TestContext.CancellationToken); + TestHelper.DoSomething("test", 42, TestContext.CancellationToken); + } + } + """; + + await VerifyCS.VerifyCodeFixAsync(code, fixedCode); + } +} diff --git a/test/UnitTests/MSTest.Analyzers.UnitTests/GlobalTestFixtureShouldBeValidAnalyzerTests.cs b/test/UnitTests/MSTest.Analyzers.UnitTests/GlobalTestFixtureShouldBeValidAnalyzerTests.cs new file mode 100644 index 0000000000..74f754c64e --- /dev/null +++ b/test/UnitTests/MSTest.Analyzers.UnitTests/GlobalTestFixtureShouldBeValidAnalyzerTests.cs @@ -0,0 +1,301 @@ +// 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.GlobalTestFixtureShouldBeValidAnalyzer, + Microsoft.CodeAnalysis.Testing.EmptyCodeFixProvider>; + +namespace MSTest.Analyzers.Test; + +[TestClass] +public sealed class GlobalTestFixtureShouldBeValidAnalyzerTests +{ + [TestMethod] + public async Task WhenGlobalTestInitializeIsValid_NoDiagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [GlobalTestInitialize] + public static void GlobalTestInitialize(TestContext testContext) + { + } + } + """; + + await VerifyCS.VerifyCodeFixAsync(code, code); + } + + [TestMethod] + public async Task WhenGlobalTestCleanupIsValid_NoDiagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [GlobalTestCleanup] + public static void GlobalTestCleanup(TestContext testContext) + { + } + } + """; + + await VerifyCS.VerifyCodeFixAsync(code, code); + } + + [TestMethod] + public async Task WhenGlobalTestInitializeIsAsyncTask_NoDiagnostic() + { + string code = """ + using System.Threading.Tasks; + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [GlobalTestInitialize] + public static async Task GlobalTestInitialize(TestContext testContext) + { + await Task.Delay(100); + } + } + """; + + await VerifyCS.VerifyCodeFixAsync(code, code); + } + + [TestMethod] + public async Task WhenGlobalTestCleanupIsAsyncValueTask_NoDiagnostic() + { + string code = """ + using System.Threading.Tasks; + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [GlobalTestCleanup] + public static async ValueTask GlobalTestCleanup(TestContext testContext) + { + await Task.Delay(100); + } + } + """; + + await VerifyCS.VerifyCodeFixAsync(code, code); + } + + [TestMethod] + public async Task WhenGlobalTestInitializeIsNotStatic_Diagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [GlobalTestInitialize] + public void [|GlobalTestInitialize|](TestContext testContext) + { + } + } + """; + + await VerifyCS.VerifyCodeFixAsync(code, code); + } + + [TestMethod] + public async Task WhenGlobalTestCleanupIsNotPublic_Diagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [GlobalTestCleanup] + private static void [|GlobalTestCleanup|](TestContext testContext) + { + } + } + """; + + await VerifyCS.VerifyCodeFixAsync(code, code); + } + + [TestMethod] + public async Task WhenGlobalTestInitializeHasNoParameters_Diagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [GlobalTestInitialize] + public static void [|GlobalTestInitialize|]() + { + } + } + """; + + await VerifyCS.VerifyCodeFixAsync(code, code); + } + + [TestMethod] + public async Task WhenGlobalTestCleanupHasWrongParameterType_Diagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [GlobalTestCleanup] + public static void [|GlobalTestCleanup|](string param) + { + } + } + """; + + await VerifyCS.VerifyCodeFixAsync(code, code); + } + + [TestMethod] + public async Task WhenGlobalTestInitializeHasTooManyParameters_Diagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [GlobalTestInitialize] + public static void [|GlobalTestInitialize|](TestContext testContext, string extraParam) + { + } + } + """; + + await VerifyCS.VerifyCodeFixAsync(code, code); + } + + [TestMethod] + public async Task WhenGlobalTestCleanupIsGeneric_Diagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [GlobalTestCleanup] + public static void [|GlobalTestCleanup|](TestContext testContext) + { + } + } + """; + + await VerifyCS.VerifyCodeFixAsync(code, code); + } + + [TestMethod] + public async Task WhenGlobalTestInitializeIsAsyncVoid_Diagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [GlobalTestInitialize] + public static async void [|GlobalTestInitialize|](TestContext testContext) + { + } + } + """; + + await VerifyCS.VerifyCodeFixAsync(code, code); + } + + [TestMethod] + public async Task WhenGlobalTestCleanupReturnsInt_Diagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [GlobalTestCleanup] + public static int [|GlobalTestCleanup|](TestContext testContext) + { + return 0; + } + } + """; + + await VerifyCS.VerifyCodeFixAsync(code, code); + } + + [TestMethod] + public async Task WhenGlobalTestCleanupInInternalClassWithoutDiscoverInternals_Diagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + internal class MyTestClass + { + [GlobalTestCleanup] + public static void [|GlobalTestCleanup|](TestContext testContext) + { + } + } + """; + + await VerifyCS.VerifyCodeFixAsync(code, code); + } + + [TestMethod] + public async Task WhenGlobalTestInitializeInStaticClass_NoDiagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public static class MyTestClass + { + [GlobalTestInitialize] + public static void GlobalTestInitialize(TestContext testContext) + { + } + } + """; + + await VerifyCS.VerifyCodeFixAsync(code, code); + } + + [TestMethod] + public async Task WhenGlobalTestCleanupInClassWithoutTestClassAttribute_Diagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + public class MyTestClass + { + [GlobalTestCleanup] + public static void [|GlobalTestCleanup|](TestContext testContext) + { + } + } + """; + + await VerifyCS.VerifyCodeFixAsync(code, code); + } +} diff --git a/test/UnitTests/MSTest.Analyzers.UnitTests/IgnoreStringMethodReturnValueAnalyzerTests.cs b/test/UnitTests/MSTest.Analyzers.UnitTests/IgnoreStringMethodReturnValueAnalyzerTests.cs new file mode 100644 index 0000000000..821c2326af --- /dev/null +++ b/test/UnitTests/MSTest.Analyzers.UnitTests/IgnoreStringMethodReturnValueAnalyzerTests.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 VerifyCS = MSTest.Analyzers.Test.CSharpCodeFixVerifier< + MSTest.Analyzers.IgnoreStringMethodReturnValueAnalyzer, + Microsoft.CodeAnalysis.Testing.EmptyCodeFixProvider>; + +namespace MSTest.Analyzers.Test; + +[TestClass] +public sealed class IgnoreStringMethodReturnValueAnalyzerTests +{ + [TestMethod] + public async Task WhenStringMethodReturnValueIsUsed_NoDiagnostic() + { + string code = """ + using System; + + public class TestClass + { + public void TestMethod() + { + string str = "Hello World"; + + // Return values are used - should not trigger diagnostic + bool result1 = str.Contains("Hello"); + bool result2 = str.StartsWith("Hello"); + bool result3 = str.EndsWith("World"); + + // Used in conditions + if (str.Contains("Hello")) + { + Console.WriteLine("Found"); + } + + // Used in expressions + bool combined = str.StartsWith("Hello") && str.EndsWith("World"); + + // Used in return statements + bool IsValid() => str.Contains("test"); + + // Used in assignments with method chaining + bool hasHello = str.ToLower().Contains("hello"); + } + } + """; + + await VerifyCS.VerifyAnalyzerAsync(code); + } + + [TestMethod] + public async Task WhenStringMethodReturnValueIsIgnored_Diagnostic() + { + string code = """ + using System; + + public class TestClass + { + public void TestMethod() + { + string str = "Hello World"; + + // Return values are ignored - should trigger diagnostics + [|str.Contains("Hello")|]; + [|str.StartsWith("Hello")|]; + [|str.EndsWith("World")|]; + + // Multiple calls on same line + [|str.Contains("test")|]; [|str.StartsWith("test")|]; + } + } + """; + + await VerifyCS.VerifyAnalyzerAsync(code); + } + + [TestMethod] + public async Task WhenNonStringMethodsCalled_NoDiagnostic() + { + string code = """ + using System; + using System.Collections.Generic; + + public class TestClass + { + public void TestMethod() + { + var list = new List(); + + // These are not string methods, should not trigger + list.Add("test"); + Console.WriteLine("test"); + + // Custom Contains method - should not trigger + var custom = new CustomClass(); + custom.Contains("test"); + } + } + + public class CustomClass + { + public bool Contains(string value) => true; + } + """; + + await VerifyCS.VerifyAnalyzerAsync(code); + } + + [TestMethod] + public async Task WhenStringMethodsFromOtherSources_Diagnostic() + { + string code = """ + public class TestClass + { + public void TestMethod() + { + string str1 = "test"; + string str2 = GetString(); + + // Return values from different sources are ignored + [|str1.Contains("e")|]; + [|str2.StartsWith("t")|]; + [|GetString().EndsWith("t")|]; + [|"literal".Contains("l")|]; + } + + private string GetString() => "test"; + } + """; + + await VerifyCS.VerifyAnalyzerAsync(code); + } + + [TestMethod] + public async Task WhenStringMethodsWithParameters_Diagnostic() + { + string code = """ + using System; + + public class TestClass + { + public void TestMethod() + { + string str = "Hello World"; + + // Methods with different parameter overloads + [|str.Contains("Hello")|]; + [|str.Contains("Hello", StringComparison.OrdinalIgnoreCase)|]; + [|str.StartsWith("Hello")|]; + [|str.StartsWith("Hello", StringComparison.OrdinalIgnoreCase)|]; + [|str.EndsWith("World")|]; + [|str.EndsWith("World", StringComparison.OrdinalIgnoreCase)|]; + } + } + """; + + 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 cc5a99e741..0f5fe782b9 100644 --- a/test/UnitTests/MSTest.Analyzers.UnitTests/MSTest.Analyzers.UnitTests.csproj +++ b/test/UnitTests/MSTest.Analyzers.UnitTests/MSTest.Analyzers.UnitTests.csproj @@ -1,7 +1,9 @@  - net6.0 + + net472;net6.0 + net6.0 true true @@ -22,7 +24,9 @@ - + + + diff --git a/test/UnitTests/MSTest.Analyzers.UnitTests/NonNullableReferenceNotInitializedSuppressorTests.cs b/test/UnitTests/MSTest.Analyzers.UnitTests/NonNullableReferenceNotInitializedSuppressorTests.cs index 23d263569a..7fb1ece97f 100644 --- a/test/UnitTests/MSTest.Analyzers.UnitTests/NonNullableReferenceNotInitializedSuppressorTests.cs +++ b/test/UnitTests/MSTest.Analyzers.UnitTests/NonNullableReferenceNotInitializedSuppressorTests.cs @@ -1,13 +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.Collections.Immutable; - -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Testing; using VerifyCS = MSTest.Analyzers.Test.CSharpCodeFixVerifier< - MSTest.Analyzers.UnitTests.NonNullableReferenceNotInitializedSuppressorTests.DoNothingAnalyzer, + MSTest.Analyzers.NonNullableReferenceNotInitializedSuppressor, Microsoft.CodeAnalysis.Testing.EmptyCodeFixProvider>; namespace MSTest.Analyzers.UnitTests; @@ -27,20 +24,11 @@ public async Task TestContextPropertyOnTestClass_DiagnosticIsSuppressed() [TestClass] public class SomeClass { - public TestContext [|TestContext|] { get; set; } + public TestContext {|#0:TestContext|} { get; set; } } "; - // Verify issues are reported - await new VerifyCS.Test - { - TestState = { Sources = { code } }, - }.RunAsync(); - - await new TestWithSuppressor - { - TestState = { Sources = { code } }, - }.RunAsync(); + await VerifySingleSuppressionAsync(code, isSuppressed: true); } [TestMethod] @@ -54,56 +42,55 @@ public async Task TestContextPropertyOnNonTestClass_DiagnosticIsNotSuppressed() public class SomeClass { - public TestContext [|TestContext|] { get; set; } + public TestContext {|#0:TestContext|} { get; set; } } "; - // Verify issues are reported - await new VerifyCS.Test - { - TestState = { Sources = { code } }, - }.RunAsync(); - - await new TestWithSuppressor - { - TestState = { Sources = { code } }, - }.RunAsync(); + await VerifySingleSuppressionAsync(code, isSuppressed: false); } - [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 DoNothingAnalyzer : DiagnosticAnalyzer + [TestMethod] + public async Task TestContextPropertyOnTestClassConstructor_DiagnosticIsSuppressed() { - [SuppressMessage("MicrosoftCodeAnalysisDesign", "RS1017:DiagnosticId for analyzers must be a non-null constant.", Justification = "For suppression test only.")] - public static readonly DiagnosticDescriptor Rule = new(NonNullableReferenceNotInitializedSuppressor.Rule.SuppressedDiagnosticId, "Title", "Message", "Category", DiagnosticSeverity.Warning, isEnabledByDefault: true); + string code = @" +#nullable enable - public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(Rule); +using System.Threading.Tasks; +using Microsoft.VisualStudio.TestTools.UnitTesting; - public override void Initialize(AnalysisContext context) - { - context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); - context.EnableConcurrentExecution(); +[TestClass] +public class SomeClass +{ + public {|#0:SomeClass|}() + { + } - context.RegisterSymbolAction(AnalyzeSymbol, SymbolKind.Property); - } + public TestContext TestContext { get; set; } +} +"; - private void AnalyzeSymbol(SymbolAnalysisContext context) - { - } + await VerifySingleSuppressionAsync(code, isSuppressed: true); } - internal sealed class TestWithSuppressor : VerifyCS.Test + private Task VerifySingleSuppressionAsync(string source, bool isSuppressed) + => VerifyDiagnosticsAsync(source, [(0, isSuppressed)]); + + private async Task VerifyDiagnosticsAsync(string source, List<(int Location, bool IsSuppressed)> diagnostics) { - protected override IEnumerable GetDiagnosticAnalyzers() + var test = new VerifyCS.Test { - foreach (DiagnosticAnalyzer analyzer in base.GetDiagnosticAnalyzers()) - { - yield return analyzer; - } + TestCode = source, + }; - yield return new NonNullableReferenceNotInitializedSuppressor(); + foreach ((int location, bool isSuppressed) in diagnostics) + { + test.ExpectedDiagnostics.Add(DiagnosticResult.CompilerError("CS8618") + .WithLocation(location) + .WithOptions(DiagnosticOptions.IgnoreAdditionalLocations) + .WithArguments("property", "TestContext") + .WithIsSuppressed(isSuppressed)); } + + await test.RunAsync(); } } diff --git a/test/UnitTests/MSTest.Analyzers.UnitTests/PreferAssertFailOverAlwaysFalseConditionsAnalyzerTests.cs b/test/UnitTests/MSTest.Analyzers.UnitTests/PreferAssertFailOverAlwaysFalseConditionsAnalyzerTests.cs index 69f3b533a6..3b6c5b3d01 100644 --- a/test/UnitTests/MSTest.Analyzers.UnitTests/PreferAssertFailOverAlwaysFalseConditionsAnalyzerTests.cs +++ b/test/UnitTests/MSTest.Analyzers.UnitTests/PreferAssertFailOverAlwaysFalseConditionsAnalyzerTests.cs @@ -125,7 +125,7 @@ public void Test() } [TestMethod] - public async Task WhenIsNullAssertion_ValueParameterAsReferenceObjectIsNotNullable_Diagnostic() + public async Task WhenIsNullAssertion_ValueParameterAsReferenceObjectIsNotNullable_NoDiagnostic() { string code = """ using Microsoft.VisualStudio.TestTools.UnitTesting; @@ -137,7 +137,7 @@ public class TestClass public void Test() { ObjectClass obj = new ObjectClass(); - [|Assert.IsNull(obj)|]; + Assert.IsNull(obj); } } @@ -146,26 +146,8 @@ public class ObjectClass } """; - string fixedCode = """ - using Microsoft.VisualStudio.TestTools.UnitTesting; - #nullable enable - [TestClass] - public class TestClass - { - [TestMethod] - public void Test() - { - ObjectClass obj = new ObjectClass(); - Assert.Fail(); - } - } - public class ObjectClass - { - - } - """; - await VerifyCS.VerifyCodeFixAsync(code, fixedCode); + await VerifyCS.VerifyCodeFixAsync(code, code); } [TestMethod] @@ -1470,4 +1452,77 @@ public void TestMethod() await VerifyCS.VerifyCodeFixAsync(code, code); } + + [TestMethod] + public async Task WhenIsNullAssertion_ReferenceTypeInNullableDisableContext_NoDiagnostic() + { + string code = """ + using System.Diagnostics.CodeAnalysis; + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public sealed class MyTests + { + #nullable disable + [TestMethod] + public void Repro() + { + Assert.IsFalse(TryGetPool(poolKey: null, out var pool)); + Assert.IsNull(pool); + } + #nullable restore + + #nullable enable + internal bool TryGetPool(string? poolKey, [MaybeNullWhen(false)] out object pool) + { + if (poolKey is not null) + { + pool = new object(); + return true; + } + + pool = null; + return false; + } + #nullable restore + } + """; + + await VerifyCS.VerifyCodeFixAsync(code, code); + } + + [TestMethod] + public async Task WhenIsNullAssertion_ValueTypeParameter_Diagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class TestClass + { + [TestMethod] + public void Test() + { + int value = 42; + [|Assert.IsNull(value)|]; + } + } + """; + string fixedCode = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class TestClass + { + [TestMethod] + public void Test() + { + int value = 42; + Assert.Fail(); + } + } + """; + + await VerifyCS.VerifyCodeFixAsync(code, fixedCode); + } } diff --git a/test/UnitTests/MSTest.Analyzers.UnitTests/PreferConstructorOverTestInitializeAnalyzerTests.cs b/test/UnitTests/MSTest.Analyzers.UnitTests/PreferConstructorOverTestInitializeAnalyzerTests.cs index f6335eedb7..7e265f7aa1 100644 --- a/test/UnitTests/MSTest.Analyzers.UnitTests/PreferConstructorOverTestInitializeAnalyzerTests.cs +++ b/test/UnitTests/MSTest.Analyzers.UnitTests/PreferConstructorOverTestInitializeAnalyzerTests.cs @@ -251,4 +251,156 @@ public MyTestClass(int y) await VerifyCS.VerifyCodeFixAsync(code, fixedCode); } + + [TestMethod] + public async Task WhenTestClassHasStaticCtorAndTestInitialize_Diagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + private object _instanceVariable; + + static MyTestClass() + { + } + + [TestInitialize] + public void [|MyTestInit|]() + { + _instanceVariable = new object(); + } + } + """; + string fixedCode = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + private object _instanceVariable; + + static MyTestClass() + { + } + + public MyTestClass() + { + _instanceVariable = new object(); + } + } + """; + + await VerifyCS.VerifyCodeFixAsync(code, fixedCode); + } + + [TestMethod] + public async Task WhenTestClassHasFieldsAndMethods_ConstructorPlacedAfterFields() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public sealed class Test1 + { + private object _instanceVariable; + + [TestInitialize] + public void [|Initialize|]() + { + _instanceVariable = new object(); + } + + [TestMethod] + public void TestMethod1() + { + } + + private void SomePrivateMethod() + { + } + } + """; + string fixedCode = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public sealed class Test1 + { + private object _instanceVariable; + + public Test1() + { + _instanceVariable = new object(); + } + + [TestMethod] + public void TestMethod1() + { + } + + private void SomePrivateMethod() + { + } + } + """; + + await VerifyCS.VerifyCodeFixAsync(code, fixedCode); + } + + [TestMethod] + public async Task WhenTestClassHasFieldsAndTestMethodBeforeTestInitialize_ConstructorPlacedAfterFields() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public sealed class Test1 + { + private object _instanceVariable; + + [TestMethod] + public void TestMethod1() + { + } + + [TestInitialize] + public void [|Initialize|]() + { + _instanceVariable = new object(); + } + + private void SomePrivateMethod() + { + } + } + """; + string fixedCode = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public sealed class Test1 + { + private object _instanceVariable; + + [TestMethod] + public void TestMethod1() + { + } + + public Test1() + { + _instanceVariable = new object(); + } + + private void SomePrivateMethod() + { + } + } + """; + + await VerifyCS.VerifyCodeFixAsync(code, fixedCode); + } } diff --git a/test/UnitTests/MSTest.Analyzers.UnitTests/PreferDisposeOverTestCleanupAnalyzerTests.cs b/test/UnitTests/MSTest.Analyzers.UnitTests/PreferDisposeOverTestCleanupAnalyzerTests.cs index 65bb4d83c5..16a8b62191 100644 --- a/test/UnitTests/MSTest.Analyzers.UnitTests/PreferDisposeOverTestCleanupAnalyzerTests.cs +++ b/test/UnitTests/MSTest.Analyzers.UnitTests/PreferDisposeOverTestCleanupAnalyzerTests.cs @@ -29,6 +29,7 @@ public void Dispose() await VerifyCS.VerifyCodeFixAsync(code, code); } +#if NET [TestMethod] public async Task WhenTestClassHasDisposeAsync_NoDiagnostic() { @@ -49,6 +50,7 @@ public ValueTask DisposeAsync() await VerifyCS.VerifyCodeFixAsync(code, code); } +#endif [TestMethod] public async Task WhenTestClassHasTestCleanup_Diagnostic() @@ -159,7 +161,7 @@ public void Dispose() [TestMethod] public async Task WhenTestClassHasTestCleanup_AndHasDisposeInAnotherPartial_Diagnostic() { - // This scenario is currently broken. The test is to document the current behavior + // This scenario should now work correctly after fixing the codefix string code = """ using Microsoft.VisualStudio.TestTools.UnitTesting; using System; @@ -192,21 +194,146 @@ public partial class MyTestClass : IDisposable public void Dispose() { int x = 1; + int y = 1; } } [TestClass] - public partial class MyTestClass : IDisposable + public partial class MyTestClass { - public void {|CS0111:Dispose|}() - { - int y = 1; - } } """; await VerifyCS.VerifyCodeFixAsync(code, fixedCode); } + [TestMethod] + public async Task WhenTestClassHasTestCleanup_AndHasDisposeInAnotherPartialInDifferentFile_Diagnostic() + { + var test = new VerifyCS.Test + { + TestState = + { + Sources = + { + @" +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System; + +public partial class MyTestClass : IDisposable +{ + public void Dispose() + { + int x = 1; + } +}", + @" +using Microsoft.VisualStudio.TestTools.UnitTesting; + +[TestClass] +public partial class MyTestClass +{ + [TestCleanup] + public void [|MyTestCleanup|]() + { + int y = 1; + } +}", + }, + }, + FixedState = + { + Sources = + { + @" +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System; + +public partial class MyTestClass : IDisposable +{ + public void Dispose() + { + int x = 1; + int y = 1; + } +}", + @" +using Microsoft.VisualStudio.TestTools.UnitTesting; + +[TestClass] +public partial class MyTestClass +{ +}", + }, + }, + }; + + await test.RunAsync(); + } + + [TestMethod] + public async Task WhenTestClassHasTestCleanup_AndHasDisposeInAnotherPartialInDifferentFile_ReversedOrder_Diagnostic() + { + var test = new VerifyCS.Test + { + TestState = + { + Sources = + { + @" +using Microsoft.VisualStudio.TestTools.UnitTesting; + +[TestClass] +public partial class MyTestClass +{ + [TestCleanup] + public void [|MyTestCleanup|]() + { + int y = 1; + } +}", + @" +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System; + +public partial class MyTestClass : IDisposable +{ + public void Dispose() + { + int x = 1; + } +}", + }, + }, + FixedState = + { + Sources = + { + @" +using Microsoft.VisualStudio.TestTools.UnitTesting; + +[TestClass] +public partial class MyTestClass +{ +}", + @" +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System; + +public partial class MyTestClass : IDisposable +{ + public void Dispose() + { + int x = 1; + int y = 1; + } +}", + }, + }, + }; + + await test.RunAsync(); + } + [TestMethod] public async Task WhenTestClassHasTestCleanupTask_Diagnostic() { @@ -230,6 +357,7 @@ public class MyTestClass await VerifyCS.VerifyCodeFixAsync(code, code); } +#if NET [TestMethod] public async Task WhenTestClassHasTestCleanupValueTask_Diagnostic() { @@ -251,4 +379,5 @@ public class MyTestClass await VerifyCS.VerifyCodeFixAsync(code, code); } +#endif } diff --git a/test/UnitTests/MSTest.Analyzers.UnitTests/PreferTestCleanupOverDisposeAnalyzerTests.cs b/test/UnitTests/MSTest.Analyzers.UnitTests/PreferTestCleanupOverDisposeAnalyzerTests.cs index fcee06522e..c6f26187d3 100644 --- a/test/UnitTests/MSTest.Analyzers.UnitTests/PreferTestCleanupOverDisposeAnalyzerTests.cs +++ b/test/UnitTests/MSTest.Analyzers.UnitTests/PreferTestCleanupOverDisposeAnalyzerTests.cs @@ -68,6 +68,7 @@ public interface IMyInterface { } await VerifyCS.VerifyCodeFixAsync(code, fixedCode); } +#if NET [TestMethod] public async Task WhenTestClassHasDisposeAsync_Diagnostic() { @@ -143,4 +144,5 @@ public ValueTask MyTestCleanup() await VerifyCS.VerifyCodeFixAsync(code, code); } +#endif } diff --git a/test/UnitTests/MSTest.Analyzers.UnitTests/PreferTestMethodOverDataTestMethodAnalyzerTests.cs b/test/UnitTests/MSTest.Analyzers.UnitTests/PreferTestMethodOverDataTestMethodAnalyzerTests.cs new file mode 100644 index 0000000000..b642a86ca7 --- /dev/null +++ b/test/UnitTests/MSTest.Analyzers.UnitTests/PreferTestMethodOverDataTestMethodAnalyzerTests.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 VerifyCS = MSTest.Analyzers.Test.CSharpCodeFixVerifier< + MSTest.Analyzers.PreferTestMethodOverDataTestMethodAnalyzer, + MSTest.Analyzers.PreferTestMethodOverDataTestMethodFixer>; + +namespace MSTest.Analyzers.Test; + +[TestClass] +public sealed class PreferTestMethodOverDataTestMethodAnalyzerTests +{ + [TestMethod] + public async Task WhenUsingTestMethod_NoDiagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + } + } + """; + + await VerifyCS.VerifyCodeFixAsync(code, code); + } + + [TestMethod] + public async Task WhenUsingDataTestMethod_Diagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [[|DataTestMethod|]] + public void MyTestMethod() + { + } + } + """; + + string fixedCode = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + } + } + """; + + await VerifyCS.VerifyCodeFixAsync(code, fixedCode); + } + + [TestMethod] + public async Task WhenUsingDataTestMethodWithDisplayName_Diagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [[|DataTestMethod("Display Name")|]] + public void MyTestMethod() + { + } + } + """; + + string fixedCode = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod("Display Name")] + public void MyTestMethod() + { + } + } + """; + + await VerifyCS.VerifyCodeFixAsync(code, fixedCode); + } + + [TestMethod] + public async Task WhenUsingDataTestMethodWithMultipleAttributesInSameList_Diagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [Ignore, [|DataTestMethod|]] + public void MyTestMethod() + { + } + } + """; + + string fixedCode = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [Ignore, TestMethod] + public void MyTestMethod() + { + } + } + """; + + await VerifyCS.VerifyCodeFixAsync(code, fixedCode); + } + + [TestMethod] + public async Task WhenInheritingDataTestMethod_Diagnostic() + { + // TODO: Codefix doesn't handle this yet. So no codefix is offered. + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + internal sealed class [|MyDataTestMethodAttribute|] : DataTestMethodAttribute + { + } + """; + + await VerifyCS.VerifyCodeFixAsync(code, code); + } +} diff --git a/test/UnitTests/MSTest.Analyzers.UnitTests/Program.cs b/test/UnitTests/MSTest.Analyzers.UnitTests/Program.cs index 92aed8206f..aaaee854d8 100644 --- a/test/UnitTests/MSTest.Analyzers.UnitTests/Program.cs +++ b/test/UnitTests/MSTest.Analyzers.UnitTests/Program.cs @@ -3,6 +3,8 @@ using Microsoft.Testing.Extensions; +using ExecutionScope = Microsoft.VisualStudio.TestTools.UnitTesting.ExecutionScope; + [assembly: Parallelize(Scope = ExecutionScope.MethodLevel, Workers = 0)] [assembly: ClassCleanupExecution(ClassCleanupBehavior.EndOfClass)] diff --git a/test/UnitTests/MSTest.Analyzers.UnitTests/PublicTypeShouldBeTestClassAnalyzerTests.cs b/test/UnitTests/MSTest.Analyzers.UnitTests/PublicTypeShouldBeTestClassAnalyzerTests.cs index b2438a3742..6bcbaaf960 100644 --- a/test/UnitTests/MSTest.Analyzers.UnitTests/PublicTypeShouldBeTestClassAnalyzerTests.cs +++ b/test/UnitTests/MSTest.Analyzers.UnitTests/PublicTypeShouldBeTestClassAnalyzerTests.cs @@ -62,6 +62,42 @@ await VerifyCS.VerifyCodeFixAsync( fixedCode); } + [TestMethod] + public async Task WhenClassIsPublicAndHasSTATestClassAttribute_NoDiagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [STATestClass] + public class MyTestClass + { + } + """; + + await VerifyCS.VerifyCodeFixAsync( + code, + code); + } + + [TestMethod] + public async Task WhenClassIsPublicAndHasDerivedTestClassAttribute_NoDiagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + internal sealed class MyTestClassAttribute : TestClassAttribute; + + [MyTestClass] + public class MyTestClass + { + } + """; + + await VerifyCS.VerifyCodeFixAsync( + code, + code); + } + [TestMethod] public async Task WhenClassIsPublicAndNotClass_NoDiagnostic() { diff --git a/test/UnitTests/MSTest.Analyzers.UnitTests/StringAssertToAssertAnalyzerTests.cs b/test/UnitTests/MSTest.Analyzers.UnitTests/StringAssertToAssertAnalyzerTests.cs new file mode 100644 index 0000000000..bb10d1b04b --- /dev/null +++ b/test/UnitTests/MSTest.Analyzers.UnitTests/StringAssertToAssertAnalyzerTests.cs @@ -0,0 +1,405 @@ +// 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.StringAssertToAssertAnalyzer, + MSTest.Analyzers.CodeFixes.StringAssertToAssertFixer>; + +namespace MSTest.Analyzers.Test; + +[TestClass] +public sealed class StringAssertToAssertAnalyzerTests +{ + [TestMethod] + public async Task WhenStringAssertContains() + { + string code = """ + using System; + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + string value = "Hello World"; + string substring = "World"; + {|#0:StringAssert.Contains(value, substring)|}; + {|#1:StringAssert.Contains(value, substring, "message")|}; + {|#2:StringAssert.Contains(value, substring, StringComparison.Ordinal)|}; + {|#3:StringAssert.Contains(value, substring, "message", StringComparison.Ordinal)|}; + {|#4:StringAssert.Contains(value, substring, "message {0}", "arg")|}; + {|#5:StringAssert.Contains(value, substring, "message {0}", StringComparison.Ordinal, "arg")|}; + } + } + """; + + string fixedCode = """ + using System; + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + string value = "Hello World"; + string substring = "World"; + Assert.Contains(substring, value); + Assert.Contains(substring, value, "message"); + Assert.Contains(substring, value, StringComparison.Ordinal); + Assert.Contains(substring, value, StringComparison.Ordinal, "message"); + Assert.Contains(substring, value, "message {0}", "arg"); + Assert.Contains(substring, value, StringComparison.Ordinal, "message {0}", "arg"); + } + } + """; + + await VerifyCS.VerifyCodeFixAsync( + code, + [ + // /0/Test0.cs(12,9): info MSTEST0046: Use 'Assert.Contains' instead of 'StringAssert.Contains' + VerifyCS.DiagnosticIgnoringAdditionalLocations().WithLocation(0).WithArguments("Contains", "Contains"), + // /0/Test0.cs(13,9): info MSTEST0046: Use 'Assert.Contains' instead of 'StringAssert.Contains' + VerifyCS.DiagnosticIgnoringAdditionalLocations().WithLocation(1).WithArguments("Contains", "Contains"), + // /0/Test0.cs(14,9): info MSTEST0046: Use 'Assert.Contains' instead of 'StringAssert.Contains' + VerifyCS.DiagnosticIgnoringAdditionalLocations().WithLocation(2).WithArguments("Contains", "Contains"), + // /0/Test0.cs(15,9): info MSTEST0046: Use 'Assert.Contains' instead of 'StringAssert.Contains' + VerifyCS.DiagnosticIgnoringAdditionalLocations().WithLocation(3).WithArguments("Contains", "Contains"), + // /0/Test0.cs(16,9): info MSTEST0046: Use 'Assert.Contains' instead of 'StringAssert.Contains' + VerifyCS.DiagnosticIgnoringAdditionalLocations().WithLocation(4).WithArguments("Contains", "Contains"), + // /0/Test0.cs(17,9): info MSTEST0046: Use 'Assert.Contains' instead of 'StringAssert.Contains' + VerifyCS.DiagnosticIgnoringAdditionalLocations().WithLocation(5).WithArguments("Contains", "Contains"), + ], + fixedCode); + } + + [TestMethod] + public async Task WhenStringAssertStartsWith() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + string value = "Hello World"; + string substring = "Hello"; + {|#0:StringAssert.StartsWith(value, substring)|}; + } + } + """; + + string fixedCode = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + string value = "Hello World"; + string substring = "Hello"; + Assert.StartsWith(substring, value); + } + } + """; + + await VerifyCS.VerifyCodeFixAsync( + code, + // /0/Test0.cs(11,9): info MSTEST0046: Use 'Assert.StartsWith' instead of 'StringAssert.StartsWith' + VerifyCS.DiagnosticIgnoringAdditionalLocations().WithLocation(0).WithArguments("StartsWith", "StartsWith"), + fixedCode); + } + + [TestMethod] + public async Task WhenStringAssertEndsWith() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + string value = "Hello World"; + string substring = "World"; + {|#0:StringAssert.EndsWith(value, substring)|}; + } + } + """; + + string fixedCode = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + string value = "Hello World"; + string substring = "World"; + Assert.EndsWith(substring, value); + } + } + """; + + await VerifyCS.VerifyCodeFixAsync( + code, + // /0/Test0.cs(11,9): info MSTEST0046: Use 'Assert.EndsWith' instead of 'StringAssert.EndsWith' + VerifyCS.DiagnosticIgnoringAdditionalLocations().WithLocation(0).WithArguments("EndsWith", "EndsWith"), + fixedCode); + } + + [TestMethod] + public async Task WhenStringAssertMatches() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + using System.Text.RegularExpressions; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + string value = "Hello World"; + Regex pattern = new Regex("Hello.*"); + {|#0:StringAssert.Matches(value, pattern)|}; + } + } + """; + + string fixedCode = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + using System.Text.RegularExpressions; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + string value = "Hello World"; + Regex pattern = new Regex("Hello.*"); + Assert.MatchesRegex(pattern, value); + } + } + """; + + await VerifyCS.VerifyCodeFixAsync( + code, + // /0/Test0.cs(12,9): info MSTEST0046: Use 'Assert.MatchesRegex' instead of 'StringAssert.Matches' + VerifyCS.DiagnosticIgnoringAdditionalLocations().WithLocation(0).WithArguments("MatchesRegex", "Matches"), + fixedCode); + } + + [TestMethod] + public async Task WhenStringAssertDoesNotMatch() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + using System.Text.RegularExpressions; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + string value = "Hello World"; + Regex pattern = new Regex("Goodbye.*"); + {|#0:StringAssert.DoesNotMatch(value, pattern)|}; + } + } + """; + + string fixedCode = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + using System.Text.RegularExpressions; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + string value = "Hello World"; + Regex pattern = new Regex("Goodbye.*"); + Assert.DoesNotMatchRegex(pattern, value); + } + } + """; + + await VerifyCS.VerifyCodeFixAsync( + code, + // /0/Test0.cs(12,9): info MSTEST0046: Use 'Assert.DoesNotMatchRegex' instead of 'StringAssert.DoesNotMatch' + VerifyCS.DiagnosticIgnoringAdditionalLocations().WithLocation(0).WithArguments("DoesNotMatchRegex", "DoesNotMatch"), + fixedCode); + } + + [TestMethod] + public async Task WhenStringAssertContainsWithMessage() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + string value = "Hello World"; + string substring = "World"; + {|#0:StringAssert.Contains(value, substring, "Custom message")|}; + } + } + """; + + string fixedCode = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + string value = "Hello World"; + string substring = "World"; + Assert.Contains(substring, value, "Custom message"); + } + } + """; + + await VerifyCS.VerifyCodeFixAsync( + code, + // /0/Test0.cs(11,9): info MSTEST0046: Use 'Assert.Contains' instead of 'StringAssert.Contains' + VerifyCS.DiagnosticIgnoringAdditionalLocations().WithLocation(0).WithArguments("Contains", "Contains"), + fixedCode); + } + + [TestMethod] + public async Task WhenNotStringAssertMethod_NoDiagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + string value = "Hello World"; + Assert.AreEqual("expected", value); + } + } + """; + + await VerifyCS.VerifyAnalyzerAsync(code); + } + + [TestMethod] + public async Task WhenStringAssertMethodWithInsufficientArguments_NoDiagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + // This won't compile but should not crash the analyzer + // error CS1501: No overload for method 'Contains' takes 1 arguments + StringAssert.{|CS1501:Contains|}("single"); + } + } + """; + + await VerifyCS.VerifyAnalyzerAsync(code); + } + + [TestMethod] + public async Task WhenStringAssertContains_ShouldPreserveEmptyLines() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void ShouldNotRemoveEmptyLine() + { + WrappedMethodCall( + 1, + 2, + 3); + + {|#0:StringAssert.Contains("value", "v")|}; + + {|#1:StringAssert.Contains("value", "v")|}; + + {|#2:StringAssert.Contains( + "value", + "v")|}; + } + + private void WrappedMethodCall(int a, int b, int c) { } + } + """; + + string fixedCode = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void ShouldNotRemoveEmptyLine() + { + WrappedMethodCall( + 1, + 2, + 3); + + Assert.Contains("v", "value"); + + Assert.Contains("v", "value"); + + Assert.Contains( + "v", + "value"); + } + + private void WrappedMethodCall(int a, int b, int c) { } + } + """; + + await VerifyCS.VerifyCodeFixAsync( + code, + [ + // /0/Test0.cs(12,9): info MSTEST0046: Use 'Assert.Contains' instead of 'StringAssert.Contains' + VerifyCS.Diagnostic().WithLocation(0).WithArguments("Contains", "Contains"), + // /0/Test0.cs(14,9): info MSTEST0046: Use 'Assert.Contains' instead of 'StringAssert.Contains' + VerifyCS.Diagnostic().WithLocation(1).WithArguments("Contains", "Contains"), + // /0/Test0.cs(16,9): info MSTEST0046: Use 'Assert.Contains' instead of 'StringAssert.Contains' + VerifyCS.Diagnostic().WithLocation(2).WithArguments("Contains", "Contains"), + ], + fixedCode); + } +} diff --git a/test/UnitTests/MSTest.Analyzers.UnitTests/TestClassShouldBeValidAnalyzerTests.cs b/test/UnitTests/MSTest.Analyzers.UnitTests/TestClassShouldBeValidAnalyzerTests.cs index adda5b0aab..1ea92af3ff 100644 --- a/test/UnitTests/MSTest.Analyzers.UnitTests/TestClassShouldBeValidAnalyzerTests.cs +++ b/test/UnitTests/MSTest.Analyzers.UnitTests/TestClassShouldBeValidAnalyzerTests.cs @@ -192,6 +192,7 @@ internal class MyTestClass [TestMethod] public async Task WhenDiscoverInternalsAndTypeIsPrivate_Diagnostic() { + // NOTE: As we are fixing the modifier, we still prefer to make it public, even if discover internals is set. string code = """ using Microsoft.VisualStudio.TestTools.UnitTesting; @@ -214,7 +215,7 @@ private class {|#0:MyTestClass|} public class A { [TestClass] - internal class MyTestClass + public class MyTestClass { } } @@ -460,4 +461,88 @@ await VerifyCS.VerifyCodeFixAsync( .WithArguments("MyTestClass"), fixedCode); } + + [TestMethod] + public async Task WhenClassIsInternalSealed_Diagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + namespace MyNamespace + { + [TestClass] + internal sealed class {|#0:MyTestClass|} + { + [TestMethod] + public void TestMethod() + { + } + } + } + """; + string fixedCode = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + namespace MyNamespace + { + [TestClass] + public sealed class MyTestClass + { + [TestMethod] + public void TestMethod() + { + } + } + } + """; + + await VerifyCS.VerifyCodeFixAsync( + code, + VerifyCS.Diagnostic(TestClassShouldBeValidAnalyzer.TestClassShouldBeValidRule) + .WithLocation(0) + .WithArguments("MyTestClass"), + fixedCode); + } + + [TestMethod] + public async Task WhenClassIsInternalStatic_Diagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + namespace MyNamespace + { + [TestClass] + internal static class {|#0:MyTestClass|} + { + [TestMethod] + public static void TestMethod() + { + } + } + } + """; + string fixedCode = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + namespace MyNamespace + { + [TestClass] + public class MyTestClass + { + [TestMethod] + public static void TestMethod() + { + } + } + } + """; + + await VerifyCS.VerifyCodeFixAsync( + code, + VerifyCS.Diagnostic(TestClassShouldBeValidAnalyzer.TestClassShouldBeValidRule) + .WithLocation(0) + .WithArguments("MyTestClass"), + fixedCode); + } } diff --git a/test/UnitTests/MSTest.Analyzers.UnitTests/TestClassShouldHaveTestMethodAnalyzerTests.cs b/test/UnitTests/MSTest.Analyzers.UnitTests/TestClassShouldHaveTestMethodAnalyzerTests.cs index 46bff4564b..e92555de5b 100644 --- a/test/UnitTests/MSTest.Analyzers.UnitTests/TestClassShouldHaveTestMethodAnalyzerTests.cs +++ b/test/UnitTests/MSTest.Analyzers.UnitTests/TestClassShouldHaveTestMethodAnalyzerTests.cs @@ -287,4 +287,65 @@ await VerifyCS.VerifyAnalyzerAsync( .WithLocation(0) .WithArguments("Derived")); } + + [TestMethod] + public async Task WhenStaticTestClassWithGlobalTestInitialize_DoesNotHaveTestMethod_NoDiagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public static class MyTestClass + { + [GlobalTestInitialize] + public static void GlobalTestInitialize(TestContext context) + { + } + } + """; + + await VerifyCS.VerifyAnalyzerAsync(code); + } + + [TestMethod] + public async Task WhenStaticTestClassWithGlobalTestCleanup_DoesNotHaveTestMethod_NoDiagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public static class MyTestClass + { + [GlobalTestCleanup] + public static void GlobalTestCleanup(TestContext context) + { + } + } + """; + + await VerifyCS.VerifyAnalyzerAsync(code); + } + + [TestMethod] + public async Task WhenNonStaticTestClassWithGlobalTestInitialize_DoesNotHaveTestMethod_Diagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class {|#0:MyTestClass|} + { + [GlobalTestInitialize] + public static void GlobalTestInitialize(TestContext context) + { + } + } + """; + + await VerifyCS.VerifyAnalyzerAsync( + code, + VerifyCS.Diagnostic(TestClassShouldHaveTestMethodAnalyzer.TestClassShouldHaveTestMethodRule) + .WithLocation(0) + .WithArguments("MyTestClass")); + } } diff --git a/test/UnitTests/MSTest.Analyzers.UnitTests/TestCleanupShouldBeValidAnalyzerTests.cs b/test/UnitTests/MSTest.Analyzers.UnitTests/TestCleanupShouldBeValidAnalyzerTests.cs index cdfea41d81..aede7da0fb 100644 --- a/test/UnitTests/MSTest.Analyzers.UnitTests/TestCleanupShouldBeValidAnalyzerTests.cs +++ b/test/UnitTests/MSTest.Analyzers.UnitTests/TestCleanupShouldBeValidAnalyzerTests.cs @@ -292,6 +292,7 @@ await VerifyCS.VerifyCodeFixAsync( fixedCode); } +#if NET [TestMethod] public async Task WhenTestCleanupReturnTypeIsNotValid_Diagnostic() { @@ -399,6 +400,7 @@ public ValueTask TestCleanup2() await VerifyCS.VerifyAnalyzerAsync(code); } +#endif [TestMethod] public async Task WhenTestCleanupIsAsyncVoid_Diagnostic() diff --git a/test/UnitTests/MSTest.Analyzers.UnitTests/TestContextPropertyUsageAnalyzerTests.cs b/test/UnitTests/MSTest.Analyzers.UnitTests/TestContextPropertyUsageAnalyzerTests.cs new file mode 100644 index 0000000000..96a769a53e --- /dev/null +++ b/test/UnitTests/MSTest.Analyzers.UnitTests/TestContextPropertyUsageAnalyzerTests.cs @@ -0,0 +1,259 @@ +// 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.TestContextPropertyUsageAnalyzer, + Microsoft.CodeAnalysis.Testing.EmptyCodeFixProvider>; + +namespace MSTest.Analyzers.Test; + +[TestClass] +public sealed class TestContextPropertyUsageAnalyzerTests +{ + [TestMethod] + public async Task WhenTestContextPropertyAccessedInAssemblyInitialize_Diagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [AssemblyInitialize] + public static void AssemblyInit(TestContext testContext) + { + _ = [|testContext.TestData|]; + _ = [|testContext.TestDisplayName|]; + _ = [|testContext.TestName|]; + _ = [|testContext.ManagedMethod|]; + _ = [|testContext.FullyQualifiedTestClassName|]; + _ = [|testContext.ManagedType|]; + } + } + """; + + await VerifyCS.VerifyAnalyzerAsync(code); + } + + [TestMethod] + public async Task WhenTestContextPropertyAccessedInAssemblyCleanup_Diagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [AssemblyCleanup] + public static void AssemblyCleanup() + { + TestContext testContext = GetTestContext(); + _ = [|testContext.TestData|]; + _ = [|testContext.TestDisplayName|]; + _ = [|testContext.TestName|]; + _ = [|testContext.ManagedMethod|]; + _ = [|testContext.FullyQualifiedTestClassName|]; + _ = [|testContext.ManagedType|]; + } + + private static TestContext GetTestContext() => null; + } + """; + + await VerifyCS.VerifyAnalyzerAsync(code); + } + + [TestMethod] + public async Task WhenTestContextPropertyAccessedInClassInitialize_Diagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [ClassInitialize] + public static void ClassInit(TestContext testContext) + { + _ = [|testContext.TestData|]; + _ = [|testContext.TestDisplayName|]; + _ = [|testContext.TestName|]; + _ = [|testContext.ManagedMethod|]; + // These should NOT trigger diagnostics in class initialize + _ = testContext.FullyQualifiedTestClassName; + _ = testContext.ManagedType; + } + } + """; + + await VerifyCS.VerifyAnalyzerAsync(code); + } + + [TestMethod] + public async Task WhenTestContextPropertyAccessedInClassCleanup_Diagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [ClassCleanup] + public static void ClassCleanup() + { + TestContext testContext = GetTestContext(); + _ = [|testContext.TestData|]; + _ = [|testContext.TestDisplayName|]; + _ = [|testContext.TestName|]; + _ = [|testContext.ManagedMethod|]; + // These should NOT trigger diagnostics in class cleanup + _ = testContext.FullyQualifiedTestClassName; + _ = testContext.ManagedType; + } + + private static TestContext GetTestContext() => null; + } + """; + + await VerifyCS.VerifyAnalyzerAsync(code); + } + + [TestMethod] + public async Task WhenTestContextPropertyAccessedInTestMethod_NoDiagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + public TestContext TestContext { get; set; } + + [TestMethod] + public void TestMethod() + { + // All of these should be allowed in test methods + _ = TestContext.TestData; + _ = TestContext.TestDisplayName; + _ = TestContext.TestName; + _ = TestContext.ManagedMethod; + _ = TestContext.FullyQualifiedTestClassName; + _ = TestContext.ManagedType; + } + } + """; + + await VerifyCS.VerifyAnalyzerAsync(code); + } + + [TestMethod] + public async Task WhenTestContextPropertyAccessedInTestInitialize_NoDiagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + public TestContext TestContext { get; set; } + + [TestInitialize] + public void TestInit() + { + // All of these should be allowed in test initialize + _ = TestContext.TestData; + _ = TestContext.TestDisplayName; + _ = TestContext.TestName; + _ = TestContext.ManagedMethod; + _ = TestContext.FullyQualifiedTestClassName; + _ = TestContext.ManagedType; + } + } + """; + + await VerifyCS.VerifyAnalyzerAsync(code); + } + + [TestMethod] + public async Task WhenTestContextPropertyAccessedInTestCleanup_NoDiagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + public TestContext TestContext { get; set; } + + [TestCleanup] + public void TestCleanup() + { + // All of these should be allowed in test cleanup + _ = TestContext.TestData; + _ = TestContext.TestDisplayName; + _ = TestContext.TestName; + _ = TestContext.ManagedMethod; + _ = TestContext.FullyQualifiedTestClassName; + _ = TestContext.ManagedType; + } + } + """; + + await VerifyCS.VerifyAnalyzerAsync(code); + } + +#if NETFRAMEWORK + [TestMethod] + public async Task WhenDataRowAndDataConnectionAccessedInNetFramework_Diagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [AssemblyInitialize] + public static void AssemblyInit(TestContext testContext) + { + _ = [|testContext.DataRow|]; + _ = [|testContext.DataConnection|]; + } + } + """; + + await VerifyCS.VerifyAnalyzerAsync(code); + } +#endif + + [TestMethod] + public async Task WhenAllowedPropertiesAccessedInFixtureMethods_NoDiagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [AssemblyInitialize] + public static void AssemblyInit(TestContext testContext) + { + // These properties should be allowed in fixture methods + _ = testContext.Properties; + testContext.WriteLine("test"); + testContext.Write("test"); + } + + [ClassInitialize] + public static void ClassInit(TestContext testContext) + { + // These properties should be allowed in class initialize + _ = testContext.FullyQualifiedTestClassName; + _ = testContext.ManagedType; + _ = testContext.Properties; + } + } + """; + + await VerifyCS.VerifyAnalyzerAsync(code); + } +} diff --git a/test/UnitTests/MSTest.Analyzers.UnitTests/TestContextShouldBeValidAnalyzerTests.cs b/test/UnitTests/MSTest.Analyzers.UnitTests/TestContextShouldBeValidAnalyzerTests.cs index efd9c1c560..ad6598ca1a 100644 --- a/test/UnitTests/MSTest.Analyzers.UnitTests/TestContextShouldBeValidAnalyzerTests.cs +++ b/test/UnitTests/MSTest.Analyzers.UnitTests/TestContextShouldBeValidAnalyzerTests.cs @@ -472,4 +472,50 @@ public class MyTestClass await VerifyCS.VerifyCodeFixAsync(code, code); } + + [TestMethod] + public async Task WhenTestContextAssignedInConstructorWithNullCheck_NoDiagnostic() + { + string code = """ + using System; + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + private readonly TestContext _testContext; + + public MyTestClass(TestContext testContext) + { + _testContext = testContext ?? throw new ArgumentNullException(nameof(testContext)); + } + + public TestContext TestContext => _testContext; + } + """; + + await VerifyCS.VerifyCodeFixAsync(code, code); + } + + [TestMethod] + public async Task WhenTestContextPropertyAssignedInConstructorWithNullCheck_NoDiagnostic() + { + string code = """ + using System; + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + public MyTestClass(TestContext testContext) + { + TestContext = testContext ?? throw new ArgumentNullException(nameof(testContext)); + } + + public TestContext TestContext { get; } + } + """; + + await VerifyCS.VerifyCodeFixAsync(code, code); + } } diff --git a/test/UnitTests/MSTest.Analyzers.UnitTests/TestInitializeShouldBeValidAnalyzerTests.cs b/test/UnitTests/MSTest.Analyzers.UnitTests/TestInitializeShouldBeValidAnalyzerTests.cs index d8eb9cbe5e..05367fc67d 100644 --- a/test/UnitTests/MSTest.Analyzers.UnitTests/TestInitializeShouldBeValidAnalyzerTests.cs +++ b/test/UnitTests/MSTest.Analyzers.UnitTests/TestInitializeShouldBeValidAnalyzerTests.cs @@ -290,6 +290,7 @@ await VerifyCS.VerifyCodeFixAsync( fixedCode); } +#if NET [TestMethod] public async Task WhenTestInitializeReturnTypeIsNotValid_Diagnostic() { @@ -397,6 +398,7 @@ public ValueTask TestInitialize2() await VerifyCS.VerifyAnalyzerAsync(code); } +#endif [TestMethod] public async Task WhenTestInitializeIsAsyncVoid_Diagnostic() diff --git a/test/UnitTests/MSTest.Analyzers.UnitTests/TestMethodShouldBeValidAnalyzerTests.cs b/test/UnitTests/MSTest.Analyzers.UnitTests/TestMethodShouldBeValidAnalyzerTests.cs index 05977b87d7..b28d976280 100644 --- a/test/UnitTests/MSTest.Analyzers.UnitTests/TestMethodShouldBeValidAnalyzerTests.cs +++ b/test/UnitTests/MSTest.Analyzers.UnitTests/TestMethodShouldBeValidAnalyzerTests.cs @@ -38,12 +38,18 @@ public async Task WhenTestMethodIsNotPublic_Diagnostic(string accessibility) { string code = $$""" using Microsoft.VisualStudio.TestTools.UnitTesting; + using System.Threading.Tasks; [TestClass] public class MyTestClass { [TestMethod] - {{accessibility}} void [|MyTestMethod|]() + {{accessibility}} void [|MyTestMethod1|]() + { + } + + [TestMethod] + {{accessibility}} async Task [|MyTestMethod2|]() { } } @@ -52,12 +58,18 @@ public class MyTestClass string fixedCode = """ using Microsoft.VisualStudio.TestTools.UnitTesting; + using System.Threading.Tasks; [TestClass] public class MyTestClass { [TestMethod] - public void MyTestMethod() + public void MyTestMethod1() + { + } + + [TestMethod] + public async Task MyTestMethod2() { } } @@ -247,6 +259,7 @@ public class MyTestClass await VerifyCS.VerifyAnalyzerAsync(code); } +#if NET [TestMethod] public async Task WhenTestMethodReturnTypeIsNotValid_Diagnostic() { @@ -346,6 +359,7 @@ public ValueTask MyTestMethod2() await VerifyCS.VerifyCodeFixAsync(code, code); } +#endif [TestMethod] public async Task WhenTestMethodIsAsyncVoid_Diagnostic() diff --git a/test/UnitTests/MSTest.Analyzers.UnitTests/TypeContainingTestMethodShouldBeATestClassAnalyzer.cs b/test/UnitTests/MSTest.Analyzers.UnitTests/TypeContainingTestMethodShouldBeATestClassAnalyzer.cs index ebbd1131ab..bec343fbaf 100644 --- a/test/UnitTests/MSTest.Analyzers.UnitTests/TypeContainingTestMethodShouldBeATestClassAnalyzer.cs +++ b/test/UnitTests/MSTest.Analyzers.UnitTests/TypeContainingTestMethodShouldBeATestClassAnalyzer.cs @@ -290,4 +290,176 @@ public void TestMethod1() await VerifyCS.VerifyCodeFixAsync(code, fixedCode); } + + [TestMethod] + public async Task WhenStructWithTestMethod_Diagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + public struct [|TestStruct|] + { + [TestMethod] + public void TestMethod1() + { + } + } + """; + + string fixedCode = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class TestStruct + { + [TestMethod] + public void TestMethod1() + { + } + } + """; + + await VerifyCS.VerifyCodeFixAsync(code, fixedCode); + } + + [TestMethod] + public async Task WhenStructWithoutTestMethod_NoDiagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + public struct TestStruct + { + public void RegularMethod() + { + } + } + """; + + await VerifyCS.VerifyAnalyzerAsync(code); + } + + [TestMethod] + public async Task WhenRecordStructWithTestMethod_Diagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + public record struct [|TestRecordStruct|] + { + [TestMethod] + public void TestMethod1() + { + } + } + """; + + string fixedCode = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public record TestRecordStruct + { + [TestMethod] + public void TestMethod1() + { + } + } + """; + + await VerifyCS.VerifyCodeFixAsync(code, fixedCode); + } + + [TestMethod] + public async Task WhenRecordClassWithTestMethod_Diagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + public record class [|TestRecordClass|] + { + [TestMethod] + public void TestMethod1() + { + } + } + """; + + string fixedCode = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public record class TestRecordClass + { + [TestMethod] + public void TestMethod1() + { + } + } + """; + + await VerifyCS.VerifyCodeFixAsync(code, fixedCode); + } + + [TestMethod] + public async Task WhenRecordWithTestMethod_Diagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + public record [|TestRecord|] + { + [TestMethod] + public void TestMethod1() + { + } + } + """; + + string fixedCode = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public record TestRecord + { + [TestMethod] + public void TestMethod1() + { + } + } + """; + + await VerifyCS.VerifyCodeFixAsync(code, fixedCode); + } + + [TestMethod] + public async Task WhenReadonlyRecordStructWithTestMethod_Diagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + public readonly record struct [|TestRecordStruct|] + { + [TestMethod] + public void TestMethod1() + { + } + } + """; + + string fixedCode = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public record TestRecordStruct + { + [TestMethod] + public void TestMethod1() + { + } + } + """; + + await VerifyCS.VerifyCodeFixAsync(code, fixedCode); + } } diff --git a/test/UnitTests/MSTest.Analyzers.UnitTests/UnusedParameterSuppressorTests.cs b/test/UnitTests/MSTest.Analyzers.UnitTests/UnusedParameterSuppressorTests.cs new file mode 100644 index 0000000000..d620499902 --- /dev/null +++ b/test/UnitTests/MSTest.Analyzers.UnitTests/UnusedParameterSuppressorTests.cs @@ -0,0 +1,298 @@ +// 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; +using Microsoft.CodeAnalysis.Diagnostics; + +using VerifyCS = MSTest.Analyzers.Test.CSharpCodeFixVerifier< + MSTest.Analyzers.UnitTests.UnusedParameterSuppressorTests.WarnForUnusedParameters, + Microsoft.CodeAnalysis.Testing.EmptyCodeFixProvider>; + +namespace MSTest.Analyzers.UnitTests; + +[TestClass] +public sealed class UnusedParameterSuppressorTests +{ + [TestMethod] + public async Task AssemblyInitializeWithUnusedTestContext_DiagnosticIsSuppressed() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class SomeClass + { + [AssemblyInitialize] + public static void Initialize(TestContext {|#0:context|}) + { + // TestContext parameter is unused but required by MSTest + } + } + """; + + // Verify issue is reported without suppressor + await new VerifyCS.Test + { + TestState = { Sources = { code } }, + ExpectedDiagnostics = + { + VerifyCS.Diagnostic(WarnForUnusedParameters.Rule) + .WithLocation(0) + .WithArguments("context") + .WithIsSuppressed(false), + }, + }.RunAsync(); + + // Verify issue is suppressed with suppressor + await new TestWithSuppressor + { + TestState = { Sources = { code } }, + ExpectedDiagnostics = + { + VerifyCS.Diagnostic(WarnForUnusedParameters.Rule) + .WithLocation(0) + .WithArguments("context") + .WithIsSuppressed(true), + }, + }.RunAsync(); + } + + [TestMethod] + public async Task ClassInitializeWithUnusedTestContext_DiagnosticIsSuppressed() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class SomeClass + { + [ClassInitialize] + public static void Initialize(TestContext {|#0:context|}) + { + // TestContext parameter is unused but required by MSTest + } + } + """; + + // Verify issue is reported without suppressor + await new VerifyCS.Test + { + TestState = { Sources = { code } }, + ExpectedDiagnostics = + { + VerifyCS.Diagnostic(WarnForUnusedParameters.Rule) + .WithLocation(0) + .WithArguments("context") + .WithIsSuppressed(false), + }, + }.RunAsync(); + + // Verify issue is suppressed with suppressor + await new TestWithSuppressor + { + TestState = { Sources = { code } }, + ExpectedDiagnostics = + { + VerifyCS.Diagnostic(WarnForUnusedParameters.Rule) + .WithLocation(0) + .WithArguments("context") + .WithIsSuppressed(true), + }, + }.RunAsync(); + } + + [TestMethod] + public async Task GlobalTestInitializeWithUnusedTestContext_DiagnosticIsSuppressed() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public static class SomeClass + { + [GlobalTestInitialize] + public static void Initialize(TestContext [|context|]) + { + // TestContext parameter is unused but required by MSTest + } + } + """; + + // Verify issue is reported without suppressor + await new VerifyCS.Test + { + TestState = { Sources = { code } }, + }.RunAsync(); + + // Verify issue is suppressed with suppressor + await new TestWithSuppressor + { + TestState = { Sources = { code } }, + }.RunAsync(); + } + + [TestMethod] + public async Task GlobalTestCleanupWithUnusedTestContext_DiagnosticIsSuppressed() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public static class SomeClass + { + [GlobalTestCleanup] + public static void Cleanup(TestContext [|context|]) + { + // TestContext parameter is unused but required by MSTest + } + } + """; + + // Verify issue is reported without suppressor + await new VerifyCS.Test + { + TestState = { Sources = { code } }, + }.RunAsync(); + + // Verify issue is suppressed with suppressor + await new TestWithSuppressor + { + TestState = { Sources = { code } }, + }.RunAsync(); + } + + [TestMethod] + public async Task TestMethodWithUnusedParameter_DiagnosticIsNotSuppressed() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class SomeClass + { + [TestMethod] + public void TestMethod(int {|#0:unusedParam|}) + { + // This should not be suppressed + } + } + """; + + // Verify issue is reported without suppressor + await new VerifyCS.Test + { + TestState = { Sources = { code } }, + ExpectedDiagnostics = + { + VerifyCS.Diagnostic(WarnForUnusedParameters.Rule) + .WithLocation(0) + .WithArguments("unusedParam") + .WithIsSuppressed(false), + }, + }.RunAsync(); + + // Verify issue is still reported with suppressor (not suppressed) + await new TestWithSuppressor + { + TestState = { Sources = { code } }, + ExpectedDiagnostics = + { + VerifyCS.Diagnostic(WarnForUnusedParameters.Rule) + .WithLocation(0) + .WithArguments("unusedParam") + .WithIsSuppressed(false), + }, + }.RunAsync(); + } + + [TestMethod] + public async Task RegularMethodWithUnusedTestContext_DiagnosticIsNotSuppressed() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class SomeClass + { + public void RegularMethod(TestContext {|#0:context|}) + { + // This should not be suppressed as it's not AssemblyInitialize or ClassInitialize + } + } + """; + + // Verify issue is reported without suppressor + await new VerifyCS.Test + { + TestState = { Sources = { code } }, + ExpectedDiagnostics = + { + VerifyCS.Diagnostic(WarnForUnusedParameters.Rule) + .WithLocation(0) + .WithArguments("context") + .WithIsSuppressed(false), + }, + }.RunAsync(); + + // Verify issue is still reported with suppressor (not suppressed) + await new TestWithSuppressor + { + TestState = { Sources = { code } }, + ExpectedDiagnostics = + { + VerifyCS.Diagnostic(WarnForUnusedParameters.Rule) + .WithLocation(0) + .WithArguments("context") + .WithIsSuppressed(false), + }, + }.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 WarnForUnusedParameters : DiagnosticAnalyzer + { + [SuppressMessage("MicrosoftCodeAnalysisDesign", "RS1017:DiagnosticId for analyzers must be a non-null constant.", Justification = "For suppression test only.")] + public static readonly DiagnosticDescriptor Rule = new(UnusedParameterSuppressor.Rule.SuppressedDiagnosticId, "Remove unused parameter", "Remove unused parameter '{0}' if it is not part of a shipped public API", "Style", DiagnosticSeverity.Warning, isEnabledByDefault: true); + + public override ImmutableArray SupportedDiagnostics => [Rule]; + + public override void Initialize(AnalysisContext context) + { + context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); + context.EnableConcurrentExecution(); + + context.RegisterSymbolAction(AnalyzeSymbol, SymbolKind.Parameter); + } + + private static void AnalyzeSymbol(SymbolAnalysisContext context) + { + if (context.Symbol is IParameterSymbol parameter) + { + // Simple mock: report all parameters as unused for testing purposes + var diagnostic = Diagnostic.Create( + Rule, + parameter.Locations.FirstOrDefault(), + parameter.Name); + context.ReportDiagnostic(diagnostic); + } + } + } + + internal sealed class TestWithSuppressor : VerifyCS.Test + { + protected override IEnumerable GetDiagnosticAnalyzers() + { + foreach (DiagnosticAnalyzer analyzer in base.GetDiagnosticAnalyzers()) + { + yield return analyzer; + } + + yield return new UnusedParameterSuppressor(); + } + } +} diff --git a/test/UnitTests/MSTest.Analyzers.UnitTests/UseAsyncSuffixTestFixtureMethodSuppressorTests.cs b/test/UnitTests/MSTest.Analyzers.UnitTests/UseAsyncSuffixTestFixtureMethodSuppressorTests.cs index 069d599c3e..d72f02d562 100644 --- a/test/UnitTests/MSTest.Analyzers.UnitTests/UseAsyncSuffixTestFixtureMethodSuppressorTests.cs +++ b/test/UnitTests/MSTest.Analyzers.UnitTests/UseAsyncSuffixTestFixtureMethodSuppressorTests.cs @@ -26,22 +26,22 @@ public async Task AsyncTestFixtureMethodsWithoutSuffix_DiagnosticIsSuppressed() public class SomeClass { [AssemblyInitialize] - public static async Task [|AssemblyInitialize|]() { } + public static async Task {|#0:AssemblyInitialize|}() { } [AssemblyCleanup] - public static async Task [|AssemblyCleanup|]() { } + public static async Task {|#1:AssemblyCleanup|}() { } [ClassInitialize] - public static async Task [|ClassInitialize|]() { } + public static async Task {|#2:ClassInitialize|}() { } [ClassCleanup] - public static async Task [|ClassCleanup|]() { } + public static async Task {|#3:ClassCleanup|}() { } [TestInitialize] - public async Task [|TestInitialize|]() { } + public async Task {|#4:TestInitialize|}() { } [TestCleanup] - public async Task [|TestCleanup|]() { } + public async Task {|#5:TestCleanup|}() { } } "; @@ -49,11 +49,101 @@ public class SomeClass await new VerifyCS.Test { TestState = { Sources = { code } }, + ExpectedDiagnostics = + { + VerifyCS.Diagnostic(WarnForMissingAsyncSuffix.Rule) + .WithLocation(0) + .WithIsSuppressed(false), + VerifyCS.Diagnostic(WarnForMissingAsyncSuffix.Rule) + .WithLocation(1) + .WithIsSuppressed(false), + VerifyCS.Diagnostic(WarnForMissingAsyncSuffix.Rule) + .WithLocation(2) + .WithIsSuppressed(false), + VerifyCS.Diagnostic(WarnForMissingAsyncSuffix.Rule) + .WithLocation(3) + .WithIsSuppressed(false), + VerifyCS.Diagnostic(WarnForMissingAsyncSuffix.Rule) + .WithLocation(4) + .WithIsSuppressed(false), + VerifyCS.Diagnostic(WarnForMissingAsyncSuffix.Rule) + .WithLocation(5) + .WithIsSuppressed(false), + }, + }.RunAsync(); + + await new TestWithSuppressor + { + TestState = { Sources = { code } }, + ExpectedDiagnostics = + { + VerifyCS.Diagnostic(WarnForMissingAsyncSuffix.Rule) + .WithLocation(0) + .WithIsSuppressed(true), + VerifyCS.Diagnostic(WarnForMissingAsyncSuffix.Rule) + .WithLocation(1) + .WithIsSuppressed(true), + VerifyCS.Diagnostic(WarnForMissingAsyncSuffix.Rule) + .WithLocation(2) + .WithIsSuppressed(true), + VerifyCS.Diagnostic(WarnForMissingAsyncSuffix.Rule) + .WithLocation(3) + .WithIsSuppressed(true), + VerifyCS.Diagnostic(WarnForMissingAsyncSuffix.Rule) + .WithLocation(4) + .WithIsSuppressed(true), + VerifyCS.Diagnostic(WarnForMissingAsyncSuffix.Rule) + .WithLocation(5) + .WithIsSuppressed(true), + }, + }.RunAsync(); + } + + [TestMethod] + public async Task GlobalTestMethodsWithoutAsyncSuffix_DiagnosticIsSuppressed() + { + string code = @" +using System.Threading.Tasks; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +[TestClass] +public static class SomeClass +{ + [GlobalTestInitialize] + public static async Task {|#0:GlobalTestInitialize|}(TestContext context) { } + + [GlobalTestCleanup] + public static async Task {|#1:GlobalTestCleanup|}(TestContext context) { } +} +"; + + // Verify issues Are reported + await new VerifyCS.Test + { + TestState = { Sources = { code } }, + ExpectedDiagnostics = + { + VerifyCS.Diagnostic(WarnForMissingAsyncSuffix.Rule) + .WithLocation(0) + .WithIsSuppressed(false), + VerifyCS.Diagnostic(WarnForMissingAsyncSuffix.Rule) + .WithLocation(1) + .WithIsSuppressed(false), + }, }.RunAsync(); await new TestWithSuppressor { TestState = { Sources = { code } }, + ExpectedDiagnostics = + { + VerifyCS.Diagnostic(WarnForMissingAsyncSuffix.Rule) + .WithLocation(0) + .WithIsSuppressed(true), + VerifyCS.Diagnostic(WarnForMissingAsyncSuffix.Rule) + .WithLocation(1) + .WithIsSuppressed(true), + }, }.RunAsync(); } @@ -61,33 +151,37 @@ public class SomeClass public async Task AsyncTestMethodWithSuffix_NoDiagnostic() { string code = """ + using System.Threading.Tasks; + using Microsoft.VisualStudio.TestTools.UnitTesting; - using System.Threading.Tasks; - using Microsoft.VisualStudio.TestTools.UnitTesting; + [TestClass] + public class SomeClass + { + [AssemblyInitialize] + public static async Task AssemblyInitializeAsync() { } - [TestClass] - public class SomeClass - { - [AssemblyInitialize] - public static async Task AssemblyInitializeAsync() { } + [AssemblyCleanup] + public static async Task AssemblyCleanupAsync() { } - [AssemblyCleanup] - public static async Task AssemblyCleanupAsync() { } + [ClassInitialize] + public static async Task ClassInitializeAsync() { } - [ClassInitialize] - public static async Task ClassInitializeAsync() { } + [ClassCleanup] + public static async Task ClassCleanupAsync() { } - [ClassCleanup] - public static async Task ClassCleanupAsync() { } + [TestInitialize] + public async Task TestInitializeAsync() { } - [TestInitialize] - public async Task TestInitializeAsync() { } + [TestCleanup] + public async Task TestCleanupAsync() { } - [TestCleanup] - public async Task TestCleanupAsync() { } - } + [GlobalTestInitialize] + public static async Task GlobalTestInitializeAsync(TestContext context) { } - """; + [GlobalTestCleanup] + public static async Task GlobalTestCleanupAsync(TestContext context) { } + } + """; await new VerifyCS.Test { @@ -104,7 +198,7 @@ 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 ImmutableArray SupportedDiagnostics => [Rule]; public override void Initialize(AnalysisContext context) { diff --git a/test/UnitTests/MSTest.Analyzers.UnitTests/UseAsyncSuffixTestMethodSuppressorTests.cs b/test/UnitTests/MSTest.Analyzers.UnitTests/UseAsyncSuffixTestMethodSuppressorTests.cs index ba566c293c..95acdb7338 100644 --- a/test/UnitTests/MSTest.Analyzers.UnitTests/UseAsyncSuffixTestMethodSuppressorTests.cs +++ b/test/UnitTests/MSTest.Analyzers.UnitTests/UseAsyncSuffixTestMethodSuppressorTests.cs @@ -28,7 +28,7 @@ public async Task AsyncTestMethodWithoutSuffix_DiagnosticIsSuppressed() public class SomeClass { [TestMethod] - public async Task [|TestMethod|]() { } + public async Task {|#0:TestMethod|}() { } } """; @@ -37,11 +37,13 @@ public class SomeClass await new VerifyCS.Test { TestState = { Sources = { code } }, + ExpectedDiagnostics = { VerifyCS.Diagnostic(WarnForMissingAsyncSuffix.Rule).WithLocation(0).WithIsSuppressed(false) }, }.RunAsync(); await new TestWithSuppressor { TestState = { Sources = { code } }, + ExpectedDiagnostics = { VerifyCS.Diagnostic(WarnForMissingAsyncSuffix.Rule).WithLocation(0).WithIsSuppressed(true) }, }.RunAsync(); } @@ -58,7 +60,7 @@ public async Task AsyncDataTestMethodWithoutSuffix_DiagnosticIsSuppressed() public class SomeClass { [DataTestMethod, DataRow(0)] - public async Task [|TestMethod|](int arg) { } + public async Task {|#0:TestMethod|}(int arg) { } } """; @@ -66,11 +68,13 @@ public class SomeClass await new VerifyCS.Test { TestState = { Sources = { code } }, + ExpectedDiagnostics = { VerifyCS.Diagnostic(WarnForMissingAsyncSuffix.Rule).WithLocation(0).WithIsSuppressed(false) }, }.RunAsync(); await new TestWithSuppressor { TestState = { Sources = { code } }, + ExpectedDiagnostics = { VerifyCS.Diagnostic(WarnForMissingAsyncSuffix.Rule).WithLocation(0).WithIsSuppressed(true) }, }.RunAsync(); } @@ -107,7 +111,7 @@ 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 ImmutableArray SupportedDiagnostics => [Rule]; public override void Initialize(AnalysisContext context) { diff --git a/test/UnitTests/MSTest.Analyzers.UnitTests/UseAttributeOnTestMethodAnalyzerTests.cs b/test/UnitTests/MSTest.Analyzers.UnitTests/UseAttributeOnTestMethodAnalyzerTests.cs index f7353183ad..8ddb170745 100644 --- a/test/UnitTests/MSTest.Analyzers.UnitTests/UseAttributeOnTestMethodAnalyzerTests.cs +++ b/test/UnitTests/MSTest.Analyzers.UnitTests/UseAttributeOnTestMethodAnalyzerTests.cs @@ -91,7 +91,7 @@ public void TestMethod() string fixedCode = $$""" using Microsoft.VisualStudio.TestTools.UnitTesting; - + {{MyExpectedExceptionAttributeDeclaration}} [TestClass] @@ -118,7 +118,7 @@ public async Task WhenMethodIsMarkedWithMultipleTestAttributesButNotWithTestMeth { string code = $$""" using Microsoft.VisualStudio.TestTools.UnitTesting; - + {{MyExpectedExceptionAttributeDeclaration}} [TestClass] @@ -134,7 +134,7 @@ public void TestMethod() string fixedCode = $$""" using Microsoft.VisualStudio.TestTools.UnitTesting; - + {{MyExpectedExceptionAttributeDeclaration}} [TestClass] @@ -149,7 +149,7 @@ public void TestMethod() } """; - await VerifyCS.VerifyCodeFixAsync(code, new[] { VerifyCS.Diagnostic(rule1).WithLocation(0), VerifyCS.Diagnostic(rule2).WithLocation(1) }, fixedCode); + await VerifyCS.VerifyCodeFixAsync(code, [VerifyCS.Diagnostic(rule1).WithLocation(0), VerifyCS.Diagnostic(rule2).WithLocation(1)], fixedCode); } [DynamicData(nameof(GetAttributeUsageExamples), DynamicDataSourceType.Method)] @@ -159,7 +159,7 @@ public async Task WhenMethodIsMarkedWithTestAttributeAndCustomTestMethod_NoDiagn string code = $$""" using System; using Microsoft.VisualStudio.TestTools.UnitTesting; - + {{MyExpectedExceptionAttributeDeclaration}} [TestClass] diff --git a/test/UnitTests/MSTest.Analyzers.UnitTests/UseCancellationTokenPropertyAnalyzerTests.cs b/test/UnitTests/MSTest.Analyzers.UnitTests/UseCancellationTokenPropertyAnalyzerTests.cs new file mode 100644 index 0000000000..476f35c123 --- /dev/null +++ b/test/UnitTests/MSTest.Analyzers.UnitTests/UseCancellationTokenPropertyAnalyzerTests.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.UseCancellationTokenPropertyAnalyzer, + MSTest.Analyzers.UseCancellationTokenPropertyFixer>; + +namespace MSTest.Analyzers.Test; + +[TestClass] +public sealed class UseCancellationTokenPropertyAnalyzerTests +{ + [TestMethod] + public async Task WhenUsingCancellationTokenSourceToken_ShouldReportDiagnostic() + { + const string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + using System.Threading; + using System.Threading.Tasks; + + [TestClass] + public class MyTestClass + { + public TestContext TestContext { get; set; } + + [TestMethod] + public async Task MyTest() + { + await SomeAsyncOperation([|TestContext.CancellationTokenSource|].Token); + } + + private static Task SomeAsyncOperation(CancellationToken cancellationToken) + => Task.CompletedTask; + } + """; + + const string fixedCode = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + using System.Threading; + using System.Threading.Tasks; + + [TestClass] + public class MyTestClass + { + public TestContext TestContext { get; set; } + + [TestMethod] + public async Task MyTest() + { + await SomeAsyncOperation(TestContext.CancellationToken); + } + + private static Task SomeAsyncOperation(CancellationToken cancellationToken) + => Task.CompletedTask; + } + """; + + await VerifyCS.VerifyCodeFixAsync(code, fixedCode); + } + + [TestMethod] + public async Task WhenUsingCancellationTokenSourceTokenWithVariable_ShouldReportDiagnostic() + { + const string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + using System.Threading; + using System.Threading.Tasks; + + [TestClass] + public class MyTestClass + { + public TestContext TestContext { get; set; } + + [TestMethod] + public async Task MyTest() + { + var context = TestContext; + await SomeAsyncOperation([|context.CancellationTokenSource|].Token); + } + + private static Task SomeAsyncOperation(CancellationToken cancellationToken) + => Task.CompletedTask; + } + """; + + const string fixedCode = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + using System.Threading; + using System.Threading.Tasks; + + [TestClass] + public class MyTestClass + { + public TestContext TestContext { get; set; } + + [TestMethod] + public async Task MyTest() + { + var context = TestContext; + await SomeAsyncOperation(context.CancellationToken); + } + + private static Task SomeAsyncOperation(CancellationToken cancellationToken) + => Task.CompletedTask; + } + """; + + await VerifyCS.VerifyCodeFixAsync(code, fixedCode); + } + + [TestMethod] + public async Task WhenUsingCancellationToken_ShouldNotReportDiagnostic() + { + const string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + using System.Threading; + using System.Threading.Tasks; + + [TestClass] + public class MyTestClass + { + public TestContext TestContext { get; set; } + + [TestMethod] + public async Task MyTest() + { + await SomeAsyncOperation(TestContext.CancellationToken); + } + + private static Task SomeAsyncOperation(CancellationToken cancellationToken) + => Task.CompletedTask; + } + """; + + await VerifyCS.VerifyAnalyzerAsync(code); + } + + [TestMethod] + public async Task WhenUsingDifferentTokenSource_ShouldNotReportDiagnostic() + { + const string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + using System.Threading; + using System.Threading.Tasks; + + [TestClass] + public class MyTestClass + { + private readonly CancellationTokenSource _cts = new(); + public TestContext TestContext { get; set; } + + [TestMethod] + public async Task MyTest() + { + await SomeAsyncOperation(_cts.Token); + } + + private static Task SomeAsyncOperation(CancellationToken cancellationToken) + => Task.CompletedTask; + } + """; + + await VerifyCS.VerifyAnalyzerAsync(code); + } +} diff --git a/test/UnitTests/MSTest.Analyzers.UnitTests/UseCooperativeCancellationForTimeoutAnalyzerTests.cs b/test/UnitTests/MSTest.Analyzers.UnitTests/UseCooperativeCancellationForTimeoutAnalyzerTests.cs new file mode 100644 index 0000000000..5d090b4bae --- /dev/null +++ b/test/UnitTests/MSTest.Analyzers.UnitTests/UseCooperativeCancellationForTimeoutAnalyzerTests.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 VerifyCS = MSTest.Analyzers.Test.CSharpCodeFixVerifier< + MSTest.Analyzers.UseCooperativeCancellationForTimeoutAnalyzer, + MSTest.Analyzers.UseCooperativeCancellationForTimeoutFixer>; + +namespace MSTest.Analyzers.Test; + +[TestClass] +public sealed class UseCooperativeCancellationForTimeoutAnalyzerTests +{ + [TestMethod] + public async Task WhenTimeoutAttributeHasCooperativeCancellationTrue_NoDiagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + [Timeout(5000, CooperativeCancellation = true)] + public void MyTestMethod() + { + } + } + """; + + await VerifyCS.VerifyCodeFixAsync(code, code); + } + + [TestMethod] + public async Task WhenTimeoutAttributeWithoutCooperativeCancellation_Diagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + [[|Timeout(5000)|]] + public void MyTestMethod() + { + } + } + """; + + string fixedCode = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + [Timeout(5000, CooperativeCancellation = true)] + public void MyTestMethod() + { + } + } + """; + + await VerifyCS.VerifyCodeFixAsync(code, fixedCode); + } + + [TestMethod] + public async Task WhenTimeoutAttributeWithCooperativeCancellationFalse_Diagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + [[|Timeout(5000, CooperativeCancellation = false)|]] + public void MyTestMethod() + { + } + } + """; + + string fixedCode = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + [Timeout(5000, CooperativeCancellation = true)] + public void MyTestMethod() + { + } + } + """; + + await VerifyCS.VerifyCodeFixAsync(code, fixedCode); + } + + [TestMethod] + public async Task WhenTimeoutAttributeOnNonTestMethod_Diagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [[|Timeout(5000)|]] + public void NonTestMethod() + { + } + } + """; + + string fixedCode = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [Timeout(5000, CooperativeCancellation = true)] + public void NonTestMethod() + { + } + } + """; + + await VerifyCS.VerifyCodeFixAsync(code, fixedCode); + } + + [TestMethod] + public async Task WhenTestMethodWithoutTimeoutAttribute_NoDiagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + } + } + """; + + await VerifyCS.VerifyCodeFixAsync(code, code); + } + + [TestMethod] + public async Task WhenTimeoutAttributeWithoutCooperativeCancellation_CodeFixAddsProperty() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + [{|#0:Timeout(5000)|}] + public void MyTestMethod() + { + } + } + """; + + string fixedCode = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + [Timeout(5000, CooperativeCancellation = true)] + public void MyTestMethod() + { + } + } + """; + + await VerifyCS.VerifyCodeFixAsync(code, VerifyCS.Diagnostic().WithLocation(0), fixedCode); + } + + [TestMethod] + public async Task WhenTimeoutAttributeWithCooperativeCancellationFalse_CodeFixChangesToTrue() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + [{|#0:Timeout(5000, CooperativeCancellation = false)|}] + public void MyTestMethod() + { + } + } + """; + + string fixedCode = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + [Timeout(5000, CooperativeCancellation = true)] + public void MyTestMethod() + { + } + } + """; + + await VerifyCS.VerifyCodeFixAsync(code, VerifyCS.Diagnostic().WithLocation(0), fixedCode); + } + + [TestMethod] + public async Task WhenTimeoutAttributeOnClassInitialize_Diagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [ClassInitialize] + [[|Timeout(5000)|]] + public static void MyClassInitialize(TestContext context) + { + } + } + """; + + string fixedCode = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [ClassInitialize] + [Timeout(5000, CooperativeCancellation = true)] + public static void MyClassInitialize(TestContext context) + { + } + } + """; + + await VerifyCS.VerifyCodeFixAsync(code, fixedCode); + } + + [TestMethod] + public async Task WhenTimeoutAttributeOnClassInitializeWithCooperativeCancellationTrue_NoDiagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [ClassInitialize] + [Timeout(5000, CooperativeCancellation = true)] + public static void MyClassInitialize(TestContext context) + { + } + } + """; + + await VerifyCS.VerifyCodeFixAsync(code, code); + } + + [TestMethod] + public async Task WhenTimeoutAttributeOnAssemblyInitialize_Diagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [AssemblyInitialize] + [[|Timeout(5000)|]] + public static void MyAssemblyInitialize(TestContext context) + { + } + } + """; + + string fixedCode = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [AssemblyInitialize] + [Timeout(5000, CooperativeCancellation = true)] + public static void MyAssemblyInitialize(TestContext context) + { + } + } + """; + + await VerifyCS.VerifyCodeFixAsync(code, fixedCode); + } + + [TestMethod] + public async Task WhenTimeoutAttributeOnTestInitialize_Diagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestInitialize] + [[|Timeout(5000)|]] + public void MyTestInitialize() + { + } + } + """; + + string fixedCode = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestInitialize] + [Timeout(5000, CooperativeCancellation = true)] + public void MyTestInitialize() + { + } + } + """; + + await VerifyCS.VerifyCodeFixAsync(code, fixedCode); + } + + [TestMethod] + public async Task WhenTimeoutAttributeOnTestCleanup_Diagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestCleanup] + [[|Timeout(5000)|]] + public void MyTestCleanup() + { + } + } + """; + + string fixedCode = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestCleanup] + [Timeout(5000, CooperativeCancellation = true)] + public void MyTestCleanup() + { + } + } + """; + + await VerifyCS.VerifyCodeFixAsync(code, fixedCode); + } + + [TestMethod] + public async Task WhenTimeoutAttributeOnClassCleanup_Diagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [ClassCleanup] + [[|Timeout(5000)|]] + public static void MyClassCleanup() + { + } + } + """; + + string fixedCode = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [ClassCleanup] + [Timeout(5000, CooperativeCancellation = true)] + public static void MyClassCleanup() + { + } + } + """; + + await VerifyCS.VerifyCodeFixAsync(code, fixedCode); + } + + [TestMethod] + public async Task WhenTimeoutAttributeOnAssemblyCleanup_Diagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [AssemblyCleanup] + [[|Timeout(5000)|]] + public static void MyAssemblyCleanup() + { + } + } + """; + + string fixedCode = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [AssemblyCleanup] + [Timeout(5000, CooperativeCancellation = true)] + public static void MyAssemblyCleanup() + { + } + } + """; + + await VerifyCS.VerifyCodeFixAsync(code, fixedCode); + } +} diff --git a/test/UnitTests/MSTest.Analyzers.UnitTests/UseParallelizeAttributeAnalyzerTests.cs b/test/UnitTests/MSTest.Analyzers.UnitTests/UseParallelizeAttributeAnalyzerTests.cs index 8872cb744b..9423776f70 100644 --- a/test/UnitTests/MSTest.Analyzers.UnitTests/UseParallelizeAttributeAnalyzerTests.cs +++ b/test/UnitTests/MSTest.Analyzers.UnitTests/UseParallelizeAttributeAnalyzerTests.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.CodeAnalysis; using Microsoft.CodeAnalysis.Testing; -using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter; using VerifyCS = MSTest.Analyzers.Test.CSharpCodeFixVerifier< MSTest.Analyzers.UseParallelizeAttributeAnalyzer, @@ -23,8 +21,13 @@ private static async Task VerifyAsync(string code, bool includeTestAdapter, para if (includeTestAdapter) { - // NOTE: Test constructor already adds TestFramework refs. - test.TestState.AdditionalReferences.Add(MetadataReference.CreateFromFile(typeof(MSTestExecutor).Assembly.Location)); + test.TestState.AnalyzerConfigFiles.Add(( + "/.globalconfig", + """ + is_global = true + + build_property.IsMSTestTestAdapterReferenced = true + """)); } test.ExpectedDiagnostics.AddRange(expected); diff --git a/test/UnitTests/MSTest.Analyzers.UnitTests/UseProperAssertMethodsAnalyzerTests.cs b/test/UnitTests/MSTest.Analyzers.UnitTests/UseProperAssertMethodsAnalyzerTests.cs index 0be2307acd..843ef58b12 100644 --- a/test/UnitTests/MSTest.Analyzers.UnitTests/UseProperAssertMethodsAnalyzerTests.cs +++ b/test/UnitTests/MSTest.Analyzers.UnitTests/UseProperAssertMethodsAnalyzerTests.cs @@ -1309,4 +1309,1645 @@ public void MyTestMethod() await VerifyCS.VerifyAnalyzerAsync(code); } + + [TestMethod] + public async Task WhenAssertIsTrueOrIsFalseWithWrongContainsMethod() + { + string code = """ + using System.Collections.ObjectModel; + using System.IO; + + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTests + { + [TestMethod] + public void Contains() + { + // This collection is KeyedCollection + // It implements IEnumerable, but the available Contains method searches for TKey. + // Whether or not the type of TKey and TItem match, we shouldn't raise a diagnostic as it changes semantics. + var collection = new MyKeyedCollection(); + Assert.IsFalse(collection.Contains(5)); + Assert.IsTrue(collection.Contains(5)); + } + + internal class MyKeyedCollection : KeyedCollection + { + protected override int GetKeyForItem(int item) + { + return 667; + } + } + } + """; + await VerifyCS.VerifyCodeFixAsync(code, code); + } + + #region New test cases for string methods + + [TestMethod] + public async Task WhenAssertIsTrueWithStringStartsWith() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + string myString = "Hello World"; + {|#0:Assert.IsTrue(myString.StartsWith("Hello"))|}; + } + } + """; + + string fixedCode = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + string myString = "Hello World"; + Assert.StartsWith("Hello", myString); + } + } + """; + + await VerifyCS.VerifyCodeFixAsync( + code, + // /0/Test0.cs(10,9): info MSTEST0037: Use 'Assert.StartsWith' instead of 'Assert.IsTrue' + VerifyCS.DiagnosticIgnoringAdditionalLocations().WithLocation(0).WithArguments("StartsWith", "IsTrue"), + fixedCode); + } + + [TestMethod] + public async Task WhenAssertIsTrueWithStringEndsWith() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + string myString = "Hello World"; + {|#0:Assert.IsTrue(myString.EndsWith("World"))|}; + } + } + """; + + string fixedCode = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + string myString = "Hello World"; + Assert.EndsWith("World", myString); + } + } + """; + + await VerifyCS.VerifyCodeFixAsync( + code, + // /0/Test0.cs(10,9): info MSTEST0037: Use 'Assert.EndsWith' instead of 'Assert.IsTrue' + VerifyCS.DiagnosticIgnoringAdditionalLocations().WithLocation(0).WithArguments("EndsWith", "IsTrue"), + fixedCode); + } + + [TestMethod] + public async Task WhenAssertIsTrueWithStringContains() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + string myString = "Hello World"; + {|#0:Assert.IsTrue(myString.Contains("lo Wo"))|}; + } + } + """; + + string fixedCode = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + string myString = "Hello World"; + Assert.Contains("lo Wo", myString); + } + } + """; + + await VerifyCS.VerifyCodeFixAsync( + code, + // /0/Test0.cs(10,9): info MSTEST0037: Use 'Assert.Contains' instead of 'Assert.IsTrue' + VerifyCS.DiagnosticIgnoringAdditionalLocations().WithLocation(0).WithArguments("Contains", "IsTrue"), + fixedCode); + } + + [TestMethod] + public async Task WhenAssertIsFalseWithStringStartsWith() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + string myString = "Hello World"; + {|#0:Assert.IsFalse(myString.StartsWith("Hello"))|}; + } + } + """; + + string fixedCode = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + string myString = "Hello World"; + Assert.DoesNotStartWith("Hello", myString); + } + } + """; + + await VerifyCS.VerifyCodeFixAsync( + code, + // /0/Test0.cs(10,9): info MSTEST0037: Use 'Assert.DoesNotStartWith' instead of 'Assert.IsFalse' + VerifyCS.DiagnosticIgnoringAdditionalLocations().WithLocation(0).WithArguments("DoesNotStartWith", "IsFalse"), + fixedCode); + } + + [TestMethod] + public async Task WhenAssertIsFalseWithStringEndsWith() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + string myString = "Hello World"; + {|#0:Assert.IsFalse(myString.EndsWith("World"))|}; + } + } + """; + + string fixedCode = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + string myString = "Hello World"; + Assert.DoesNotEndWith("World", myString); + } + } + """; + + await VerifyCS.VerifyCodeFixAsync( + code, + // /0/Test0.cs(10,9): info MSTEST0037: Use 'Assert.DoesNotEndWith' instead of 'Assert.IsFalse' + VerifyCS.DiagnosticIgnoringAdditionalLocations().WithLocation(0).WithArguments("DoesNotEndWith", "IsFalse"), + fixedCode); + } + + [TestMethod] + public async Task WhenAssertIsFalseWithStringContains() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + string myString = "Hello World"; + {|#0:Assert.IsFalse(myString.Contains("test"))|}; + } + } + """; + + string fixedCode = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + string myString = "Hello World"; + Assert.DoesNotContain("test", myString); + } + } + """; + + await VerifyCS.VerifyCodeFixAsync( + code, + // /0/Test0.cs(10,9): info MSTEST0037: Use 'Assert.DoesNotContain' instead of 'Assert.IsFalse' + VerifyCS.DiagnosticIgnoringAdditionalLocations().WithLocation(0).WithArguments("DoesNotContain", "IsFalse"), + fixedCode); + } + + #endregion + + #region New test cases for collection methods + + [TestMethod] + public async Task WhenAssertIsTrueWithCollectionContains() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + using System.Collections.Generic; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + var list = new List { 1, 2, 3 }; + {|#0:Assert.IsTrue(list.Contains(2))|}; + } + } + """; + + string fixedCode = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + using System.Collections.Generic; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + var list = new List { 1, 2, 3 }; + Assert.Contains(2, list); + } + } + """; + + await VerifyCS.VerifyCodeFixAsync( + code, + // /0/Test0.cs(11,9): info MSTEST0037: Use 'Assert.Contains' instead of 'Assert.IsTrue' + VerifyCS.DiagnosticIgnoringAdditionalLocations().WithLocation(0).WithArguments("Contains", "IsTrue"), + fixedCode); + } + + [TestMethod] + public async Task WhenAssertIsFalseWithCollectionContains() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + using System.Collections.Generic; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + var list = new List { 1, 2, 3 }; + {|#0:Assert.IsFalse(list.Contains(4))|}; + } + } + """; + + string fixedCode = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + using System.Collections.Generic; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + var list = new List { 1, 2, 3 }; + Assert.DoesNotContain(4, list); + } + } + """; + + await VerifyCS.VerifyCodeFixAsync( + code, + // /0/Test0.cs(11,9): info MSTEST0037: Use 'Assert.DoesNotContain' instead of 'Assert.IsFalse' + VerifyCS.DiagnosticIgnoringAdditionalLocations().WithLocation(0).WithArguments("DoesNotContain", "IsFalse"), + fixedCode); + } + + #endregion + + #region New test cases for comparisons + + [TestMethod] + public async Task WhenAssertIsTrueWithGreaterThanComparison() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + int a = 5; + int b = 3; + {|#0:Assert.IsTrue(a > b)|}; + } + } + """; + + string fixedCode = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + int a = 5; + int b = 3; + Assert.IsGreaterThan(b, a); + } + } + """; + + await VerifyCS.VerifyCodeFixAsync( + code, + // /0/Test0.cs(11,9): info MSTEST0037: Use 'Assert.IsGreaterThan' instead of 'Assert.IsTrue' + VerifyCS.DiagnosticIgnoringAdditionalLocations().WithLocation(0).WithArguments("IsGreaterThan", "IsTrue"), + fixedCode); + } + + [TestMethod] + public async Task WhenAssertIsFalseWithGreaterThanComparison() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + int a = 5; + int b = 3; + {|#0:Assert.IsFalse(a > b)|}; + } + } + """; + + string fixedCode = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + int a = 5; + int b = 3; + Assert.IsLessThanOrEqualTo(b, a); + } + } + """; + + await VerifyCS.VerifyCodeFixAsync( + code, + // /0/Test0.cs(11,9): info MSTEST0037: Use 'Assert.IsLessThanOrEqualTo' instead of 'Assert.IsFalse' + VerifyCS.DiagnosticIgnoringAdditionalLocations().WithLocation(0).WithArguments("IsLessThanOrEqualTo", "IsFalse"), + fixedCode); + } + + [TestMethod] + public async Task WhenAssertIsTrueWithEqualsComparison() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + int a = 5; + int b = 5; + {|#0:Assert.IsTrue(a == b)|}; + } + } + """; + + string fixedCode = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + int a = 5; + int b = 5; + Assert.AreEqual(b, a); + } + } + """; + + await VerifyCS.VerifyCodeFixAsync( + code, + // /0/Test0.cs(11,9): info MSTEST0037: Use 'Assert.AreEqual' instead of 'Assert.IsTrue' + VerifyCS.DiagnosticIgnoringAdditionalLocations().WithLocation(0).WithArguments("AreEqual", "IsTrue"), + fixedCode); + } + + [TestMethod] + public async Task WhenAssertIsFalseWithEqualsComparison() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + int a = 5; + int b = 3; + {|#0:Assert.IsFalse(a == b)|}; + } + } + """; + + string fixedCode = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + int a = 5; + int b = 3; + Assert.AreNotEqual(b, a); + } + } + """; + + await VerifyCS.VerifyCodeFixAsync( + code, + // /0/Test0.cs(11,9): info MSTEST0037: Use 'Assert.AreNotEqual' instead of 'Assert.IsFalse' + VerifyCS.DiagnosticIgnoringAdditionalLocations().WithLocation(0).WithArguments("AreNotEqual", "IsFalse"), + fixedCode); + } + + #endregion + + #region New test cases for collection count + + [TestMethod] + public async Task WhenAssertAreEqualWithCollectionCountZero() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + using System.Collections.Generic; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + var list = new List(); + {|#0:Assert.AreEqual(0, list.Count)|}; + Assert.AreEqual(list.Count, 0); + } + } + """; + + string fixedCode = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + using System.Collections.Generic; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + var list = new List(); + Assert.IsEmpty(list); + Assert.AreEqual(list.Count, 0); + } + } + """; + + await VerifyCS.VerifyCodeFixAsync( + code, + [ + // /0/Test0.cs(11,9): info MSTEST0037: Use 'Assert.IsEmpty' instead of 'Assert.AreEqual' + VerifyCS.DiagnosticIgnoringAdditionalLocations().WithLocation(0).WithArguments("IsEmpty", "AreEqual"), + ], + fixedCode); + } + + [TestMethod] + public async Task WhenAssertAreEqualWithCollectionCountNonZero() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + using System.Collections.Generic; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + var list = new List { 1, 2, 3 }; + int x = 3; + {|#0:Assert.AreEqual(3, list.Count)|}; + Assert.AreEqual(list.Count, 3); + {|#1:Assert.AreEqual(x, list.Count)|}; + Assert.AreEqual(list.Count, x); + } + } + """; + + string fixedCode = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + using System.Collections.Generic; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + var list = new List { 1, 2, 3 }; + int x = 3; + Assert.HasCount(3, list); + Assert.AreEqual(list.Count, 3); + Assert.HasCount(x, list); + Assert.AreEqual(list.Count, x); + } + } + """; + + await VerifyCS.VerifyCodeFixAsync( + code, + [ + // /0/Test0.cs(12,9): info MSTEST0037: Use 'Assert.HasCount' instead of 'Assert.AreEqual' + VerifyCS.DiagnosticIgnoringAdditionalLocations().WithLocation(0).WithArguments("HasCount", "AreEqual"), + // /0/Test0.cs(14,9): info MSTEST0037: Use 'Assert.HasCount' instead of 'Assert.AreEqual' + VerifyCS.DiagnosticIgnoringAdditionalLocations().WithLocation(1).WithArguments("HasCount", "AreEqual"), + ], + fixedCode); + } + + [TestMethod] + public async Task WhenAssertAreNotEqualWithCollectionCountZero() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + using System.Collections.Generic; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + var list = new List(); + Assert.AreNotEqual(0, list.Count); + } + } + """; + + await VerifyCS.VerifyAnalyzerAsync(code); + } + + [TestMethod] + public async Task WhenAssertAreNotEqualWithCollectionCountNonZero() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + using System.Collections.Generic; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + var list = new List { 1, 2, 3 }; + Assert.AreNotEqual(3, list.Count); + } + } + """; + + await VerifyCS.VerifyAnalyzerAsync(code); + } + + [TestMethod] + public async Task WhenAssertAreNotEqualWithArrayLength() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + var array = new int[] { 1, 2, 3, 4, 5 }; + Assert.AreNotEqual(5, array.Length); + } + } + """; + + await VerifyCS.VerifyAnalyzerAsync(code); + } + + [TestMethod] + public async Task WhenAssertAreEqualWithCollectionCountAndMessage_HasCount() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + using System.Collections.Generic; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + var myCollection = new List { 1, 2 }; + {|#0:Assert.AreEqual(2, myCollection.Count, "Wrong number of elements")|}; + } + } + """; + + string fixedCode = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + using System.Collections.Generic; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + var myCollection = new List { 1, 2 }; + Assert.HasCount(2, myCollection, "Wrong number of elements"); + } + } + """; + + await VerifyCS.VerifyCodeFixAsync( + code, + // /0/Test0.cs(11,9): info MSTEST0037: Use 'Assert.HasCount' instead of 'Assert.AreEqual' + VerifyCS.DiagnosticIgnoringAdditionalLocations().WithLocation(0).WithArguments("HasCount", "AreEqual"), + fixedCode); + } + + [TestMethod] + public async Task WhenAssertAreEqualWithCollectionCountZeroAndMessage_IsEmpty() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + using System.Collections.Generic; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + var list = new List(); + {|#0:Assert.AreEqual(0, list.Count, "Collection should be empty")|}; + } + } + """; + + string fixedCode = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + using System.Collections.Generic; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + var list = new List(); + Assert.IsEmpty(list, "Collection should be empty"); + } + } + """; + + await VerifyCS.VerifyCodeFixAsync( + code, + // /0/Test0.cs(11,9): info MSTEST0037: Use 'Assert.IsEmpty' instead of 'Assert.AreEqual' + VerifyCS.DiagnosticIgnoringAdditionalLocations().WithLocation(0).WithArguments("IsEmpty", "AreEqual"), + fixedCode); + } + + [TestMethod] + public async Task WhenAssertAreEqualWithCollectionCountAndMultipleParameters() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + using System.Collections.Generic; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + var list = new List { 1, 2, 3 }; + {|#0:Assert.AreEqual(3, list.Count, "Wrong count: expected {0} but was {1}", 3, list.Count)|}; + } + } + """; + + string fixedCode = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + using System.Collections.Generic; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + var list = new List { 1, 2, 3 }; + Assert.HasCount(3, list, "Wrong count: expected {0} but was {1}", 3, list.Count); + } + } + """; + + await VerifyCS.VerifyCodeFixAsync( + code, + // /0/Test0.cs(11,9): info MSTEST0037: Use 'Assert.HasCount' instead of 'Assert.AreEqual' + VerifyCS.DiagnosticIgnoringAdditionalLocations().WithLocation(0).WithArguments("HasCount", "AreEqual"), + fixedCode); + } + + [TestMethod] + public async Task WhenAssertAreEqualWithCollectionCountUsingCustomCollection() + { + string code = """ + using System; + using System.Collections; + using System.Collections.Generic; + using Microsoft.VisualStudio.TestTools.UnitTesting; + + internal sealed class MyCustomCollection : IEnumerable + { + public IEnumerator GetEnumerator() + { + throw new NotImplementedException(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + throw new NotImplementedException(); + } + + public int Count => 5; + } + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + var x = new MyCustomCollection(); + Assert.AreEqual(4, x.Count); + } + } + """; + + await VerifyCS.VerifyCodeFixAsync(code, code); + } + + [TestMethod] + public async Task WhenAssertAreEqualWithCollectionCountUsingNonGenericCollection() + { + string code = """ + using System; + using System.Collections; + using System.Collections.Generic; + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + var x = new Hashtable(); + {|#0:Assert.AreEqual(4, x.Count)|}; + } + } + """; + + string fixedCode = """ + using System; + using System.Collections; + using System.Collections.Generic; + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + var x = new Hashtable(); + Assert.HasCount(4, x); + } + } + """; + + await VerifyCS.VerifyCodeFixAsync( + code, + // /0/Test0.cs(13,9): info MSTEST0037: Use 'Assert.HasCount' instead of 'Assert.AreEqual' + VerifyCS.DiagnosticIgnoringAdditionalLocations().WithLocation(0).WithArguments("HasCount", "AreEqual"), + fixedCode); + } + + [TestMethod] + public async Task WhenAssertAreEqualWithNonGenericCollectionCountZero() + { + string code = """ + using System.Collections; + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + var hashtable = new Hashtable(); + {|#0:Assert.AreEqual(0, hashtable.Count)|}; + } + } + """; + + string fixedCode = """ + using System.Collections; + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + var hashtable = new Hashtable(); + Assert.IsEmpty(hashtable); + } + } + """; + + await VerifyCS.VerifyCodeFixAsync( + code, + VerifyCS.DiagnosticIgnoringAdditionalLocations().WithLocation(0).WithArguments("IsEmpty", "AreEqual"), + fixedCode); + } + + [TestMethod] + public async Task WhenAssertIsTrueWithNonGenericCollectionCountEqualZero() + { + string code = """ + using System.Collections; + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + var hashtable = new Hashtable(); + {|#0:Assert.IsTrue(hashtable.Count == 0)|}; + } + } + """; + + string fixedCode = """ + using System.Collections; + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + var hashtable = new Hashtable(); + Assert.IsEmpty(hashtable); + } + } + """; + + await VerifyCS.VerifyCodeFixAsync( + code, + VerifyCS.DiagnosticIgnoringAdditionalLocations().WithLocation(0).WithArguments("IsEmpty", "IsTrue"), + fixedCode); + } + + [TestMethod] + public async Task WhenAssertIsTrueWithNonGenericCollectionCountGreaterThanZero() + { + string code = """ + using System.Collections; + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + var hashtable = new Hashtable { { "key", "value" } }; + {|#0:Assert.IsTrue(hashtable.Count > 0)|}; + } + } + """; + + string fixedCode = """ + using System.Collections; + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + var hashtable = new Hashtable { { "key", "value" } }; + Assert.IsNotEmpty(hashtable); + } + } + """; + + await VerifyCS.VerifyCodeFixAsync( + code, + VerifyCS.DiagnosticIgnoringAdditionalLocations().WithLocation(0).WithArguments("IsNotEmpty", "IsTrue"), + fixedCode); + } + #endregion + + #region New test cases for collection emptiness checks + + [TestMethod] + public async Task WhenAssertIsTrueWithCollectionCountGreaterThanZero() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + using System.Collections.Generic; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + var list = new List { 1, 2, 3 }; + {|#0:Assert.IsTrue(list.Count > 0)|}; + } + } + """; + + string fixedCode = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + using System.Collections.Generic; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + var list = new List { 1, 2, 3 }; + Assert.IsNotEmpty(list); + } + } + """; + + await VerifyCS.VerifyCodeFixAsync( + code, + // /0/Test0.cs(11,9): info MSTEST0037: Use 'Assert.IsNotEmpty' instead of 'Assert.IsTrue' + VerifyCS.DiagnosticIgnoringAdditionalLocations().WithLocation(0).WithArguments("IsNotEmpty", "IsTrue"), + fixedCode); + } + + [TestMethod] + public async Task WhenAssertIsTrueWithCollectionCountNotEqualToZero() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + using System.Collections.Generic; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + var list = new List { 1, 2, 3 }; + {|#0:Assert.IsTrue(list.Count != 0)|}; + } + } + """; + + string fixedCode = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + using System.Collections.Generic; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + var list = new List { 1, 2, 3 }; + Assert.IsNotEmpty(list); + } + } + """; + + await VerifyCS.VerifyCodeFixAsync( + code, + // /0/Test0.cs(11,9): info MSTEST0037: Use 'Assert.IsNotEmpty' instead of 'Assert.IsTrue' + VerifyCS.DiagnosticIgnoringAdditionalLocations().WithLocation(0).WithArguments("IsNotEmpty", "IsTrue"), + fixedCode); + } + + [TestMethod] + public async Task WhenAssertIsTrueWithCollectionCountEqualsZero() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + using System.Collections.Generic; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + var list = new List { 1, 2, 3 }; + {|#0:Assert.IsTrue(list.Count == 0)|}; + {|#1:Assert.IsTrue(0 == list.Count)|}; + } + } + """; + + string fixedCode = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + using System.Collections.Generic; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + var list = new List { 1, 2, 3 }; + Assert.IsEmpty(list); + Assert.IsEmpty(list); + } + } + """; + + await VerifyCS.VerifyCodeFixAsync( + code, + [ + // /0/Test0.cs(11,9): info MSTEST0037: Use 'Assert.IsEmpty' instead of 'Assert.IsTrue' + VerifyCS.DiagnosticIgnoringAdditionalLocations().WithLocation(0).WithArguments("IsEmpty", "IsTrue"), + // /0/Test0.cs(12,9): info MSTEST0037: Use 'Assert.IsEmpty' instead of 'Assert.IsTrue' + VerifyCS.DiagnosticIgnoringAdditionalLocations().WithLocation(1).WithArguments("IsEmpty", "IsTrue"), + ], + fixedCode); + } + + [TestMethod] + public async Task WhenAssertIsTrueWithArrayLengthGreaterThanZero() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + var array = new int[] { 1, 2, 3 }; + {|#0:Assert.IsTrue(array.Length > 0)|}; + } + } + """; + + string fixedCode = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + var array = new int[] { 1, 2, 3 }; + Assert.IsNotEmpty(array); + } + } + """; + + await VerifyCS.VerifyCodeFixAsync( + code, + // /0/Test0.cs(10,9): info MSTEST0037: Use 'Assert.IsNotEmpty' instead of 'Assert.IsTrue' + VerifyCS.DiagnosticIgnoringAdditionalLocations().WithLocation(0).WithArguments("IsNotEmpty", "IsTrue"), + fixedCode); + } + + [TestMethod] + public async Task WhenAssertIsTrueWithArrayLengthNotEqualToZero() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + var array = new int[] { 1, 2, 3 }; + {|#0:Assert.IsTrue(array.Length != 0)|}; + } + } + """; + + string fixedCode = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + var array = new int[] { 1, 2, 3 }; + Assert.IsNotEmpty(array); + } + } + """; + + await VerifyCS.VerifyCodeFixAsync( + code, + // /0/Test0.cs(10,9): info MSTEST0037: Use 'Assert.IsNotEmpty' instead of 'Assert.IsTrue' + VerifyCS.DiagnosticIgnoringAdditionalLocations().WithLocation(0).WithArguments("IsNotEmpty", "IsTrue"), + fixedCode); + } + + [TestMethod] + public async Task WhenAssertIsTrueWithArrayLengthEqualToZero() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + var array = new int[] { 1, 2, 3 }; + {|#0:Assert.IsTrue(array.Length == 0)|}; + {|#1:Assert.IsTrue(0 == array.Length)|}; + } + } + """; + + string fixedCode = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + var array = new int[] { 1, 2, 3 }; + Assert.IsEmpty(array); + Assert.IsEmpty(array); + } + } + """; + + await VerifyCS.VerifyCodeFixAsync( + code, + [ + // /0/Test0.cs(10,9): info MSTEST0037: Use 'Assert.IsEmpty' instead of 'Assert.IsTrue' + VerifyCS.DiagnosticIgnoringAdditionalLocations().WithLocation(0).WithArguments("IsEmpty", "IsTrue"), + // /0/Test0.cs(11,9): info MSTEST0037: Use 'Assert.IsEmpty' instead of 'Assert.IsTrue' + VerifyCS.DiagnosticIgnoringAdditionalLocations().WithLocation(1).WithArguments("IsEmpty", "IsTrue"), + ], + fixedCode); + } + + [TestMethod] + public async Task WhenAssertIsTrueWithZeroNotEqualToCollectionCount() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + using System.Collections.Generic; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + var list = new List { 1, 2, 3 }; + {|#0:Assert.IsTrue(0 != list.Count)|}; + } + } + """; + + string fixedCode = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + using System.Collections.Generic; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + var list = new List { 1, 2, 3 }; + Assert.IsNotEmpty(list); + } + } + """; + + await VerifyCS.VerifyCodeFixAsync( + code, + // /0/Test0.cs(11,9): info MSTEST0037: Use 'Assert.IsNotEmpty' instead of 'Assert.IsTrue' + VerifyCS.DiagnosticIgnoringAdditionalLocations().WithLocation(0).WithArguments("IsNotEmpty", "IsTrue"), + fixedCode); + } + + [TestMethod] + public async Task WhenAssertIsFalseWithCollectionCountGreaterThanZero() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + using System.Collections.Generic; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + var list = new List { 1, 2, 3 }; + {|#0:Assert.IsFalse(list.Count > 0)|}; + } + } + """; + + string fixedCode = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + using System.Collections.Generic; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + var list = new List { 1, 2, 3 }; + Assert.IsEmpty(list); + } + } + """; + + await VerifyCS.VerifyCodeFixAsync( + code, + // /0/Test0.cs(11,9): info MSTEST0037: Use 'Assert.IsEmpty' instead of 'Assert.IsFalse' + VerifyCS.DiagnosticIgnoringAdditionalLocations().WithLocation(0).WithArguments("IsEmpty", "IsFalse"), + fixedCode); + } + + [TestMethod] + public async Task WhenAssertIsFalseWithCollectionCountEqualZero() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + using System.Collections.Generic; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + var list = new List { 1, 2, 3 }; + {|#0:Assert.IsFalse(list.Count == 0)|}; + {|#1:Assert.IsFalse(0 == list.Count)|}; + } + } + """; + + string fixedCode = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + using System.Collections.Generic; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + var list = new List { 1, 2, 3 }; + Assert.IsNotEmpty(list); + Assert.IsNotEmpty(list); + } + } + """; + + await VerifyCS.VerifyCodeFixAsync( + code, + [ + // /0/Test0.cs(11,9): info MSTEST0037: Use 'Assert.IsNotEmpty' instead of 'Assert.IsFalse' + VerifyCS.DiagnosticIgnoringAdditionalLocations().WithLocation(0).WithArguments("IsNotEmpty", "IsFalse"), + // /0/Test0.cs(12,9): info MSTEST0037: Use 'Assert.IsNotEmpty' instead of 'Assert.IsFalse' + VerifyCS.DiagnosticIgnoringAdditionalLocations().WithLocation(1).WithArguments("IsNotEmpty", "IsFalse"), + ], + fixedCode); + } + + [TestMethod] + public async Task WhenAssertIsFalseWithCollectionCountNotEqualToZero() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + using System.Collections.Generic; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + var list = new List { 1, 2, 3 }; + {|#0:Assert.IsFalse(list.Count != 0)|}; + } + } + """; + + string fixedCode = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + using System.Collections.Generic; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + var list = new List { 1, 2, 3 }; + Assert.IsEmpty(list); + } + } + """; + + await VerifyCS.VerifyCodeFixAsync( + code, + // /0/Test0.cs(11,9): info MSTEST0037: Use 'Assert.IsEmpty' instead of 'Assert.IsFalse' + VerifyCS.DiagnosticIgnoringAdditionalLocations().WithLocation(0).WithArguments("IsEmpty", "IsFalse"), + fixedCode); + } + + [TestMethod] + public async Task WhenAssertIsTrueWithCollectionCountGreaterThanNonZero_ShouldUseIsGreaterThan() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + using System.Collections.Generic; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + var list = new List { 1, 2, 3 }; + // This should use the generic comparison logic, not IsNotEmpty + {|#0:Assert.IsTrue(list.Count > 2)|}; + } + } + """; + + string fixedCode = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + using System.Collections.Generic; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + var list = new List { 1, 2, 3 }; + // This should use the generic comparison logic, not IsNotEmpty + Assert.IsGreaterThan(2, list.Count); + } + } + """; + + await VerifyCS.VerifyCodeFixAsync( + code, + // /0/Test0.cs(11,9): info MSTEST0037: Use 'Assert.IsGreaterThan' instead of 'Assert.IsTrue' + VerifyCS.DiagnosticIgnoringAdditionalLocations().WithLocation(0).WithArguments("IsGreaterThan", "IsTrue"), + fixedCode); + } + + [TestMethod] + public async Task WhenAssertIsTrueWithCollectionCountNotEqualToNonZero_ShouldUseAreNotEqual() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + using System.Collections.Generic; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + var list = new List { 1, 2, 3 }; + {|#0:Assert.IsTrue(list.Count != 5)|}; + } + } + """; + + string fixedCode = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + using System.Collections.Generic; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + var list = new List { 1, 2, 3 }; + Assert.AreNotEqual(5, list.Count); + } + } + """; + + await VerifyCS.VerifyCodeFixAsync( + code, + // /0/Test0.cs(11,9): info MSTEST0037: Use 'Assert.AreNotEqual' instead of 'Assert.IsTrue' + VerifyCS.DiagnosticIgnoringAdditionalLocations().WithLocation(0).WithArguments("AreNotEqual", "IsTrue"), + fixedCode); + } + + [TestMethod] + public async Task WhenAssertIsTrueWithNonBCLCollectionCount_ShouldUseGenericComparison() + { + string code = """ + using System; + using System.Collections; + using System.Collections.Generic; + using Microsoft.VisualStudio.TestTools.UnitTesting; + + internal sealed class MyCustomCollection : IEnumerable + { + public IEnumerator GetEnumerator() + { + throw new NotImplementedException(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + throw new NotImplementedException(); + } + + public int Count => 5; + } + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + var customCollection = new MyCustomCollection(); + // This should use the generic comparison logic since it's not a BCL collection + {|#0:Assert.IsTrue(customCollection.Count > 0)|}; + } + } + """; + + string fixedCode = """ + using System; + using System.Collections; + using System.Collections.Generic; + using Microsoft.VisualStudio.TestTools.UnitTesting; + + internal sealed class MyCustomCollection : IEnumerable + { + public IEnumerator GetEnumerator() + { + throw new NotImplementedException(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + throw new NotImplementedException(); + } + + public int Count => 5; + } + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + var customCollection = new MyCustomCollection(); + // This should use the generic comparison logic since it's not a BCL collection + Assert.IsGreaterThan(0, customCollection.Count); + } + } + """; + + await VerifyCS.VerifyCodeFixAsync( + code, + // /0/Test0.cs(25,9): info MSTEST0037: Use 'Assert.IsGreaterThan' instead of 'Assert.IsTrue' + VerifyCS.DiagnosticIgnoringAdditionalLocations().WithLocation(0).WithArguments("IsGreaterThan", "IsTrue"), + fixedCode); + } + + #endregion } 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 3fc45b7658..05f286761c 100644 --- a/test/UnitTests/MSTest.Analyzers.UnitTests/Verifiers/CSharpCodeFixVerifier`2+Test.cs +++ b/test/UnitTests/MSTest.Analyzers.UnitTests/Verifiers/CSharpCodeFixVerifier`2+Test.cs @@ -19,7 +19,13 @@ public class Test : CSharpCodeFixTest { public Test() { +#if NET462 + ReferenceAssemblies = ReferenceAssemblies.NetFramework.Net462.Default; + TestState.AdditionalReferences.Add(MetadataReference.CreateFromFile(typeof(ValueTask<>).Assembly.Location)); + TestState.AdditionalReferences.Add(MetadataReference.CreateFromFile(typeof(IAsyncDisposable).Assembly.Location)); +#else ReferenceAssemblies = ReferenceAssemblies.Net.Net60; +#endif TestState.AdditionalReferences.Add(MetadataReference.CreateFromFile(typeof(ParallelizeAttribute).Assembly.Location)); TestState.AdditionalReferences.Add(MetadataReference.CreateFromFile(typeof(TestContext).Assembly.Location)); SolutionTransforms.Add((solution, projectId) => diff --git a/test/UnitTests/MSTest.Analyzers.UnitTests/Verifiers/VisualBasicCodeFixVerifier`2+Test.cs b/test/UnitTests/MSTest.Analyzers.UnitTests/Verifiers/VisualBasicCodeFixVerifier`2+Test.cs new file mode 100644 index 0000000000..28715f1f30 --- /dev/null +++ b/test/UnitTests/MSTest.Analyzers.UnitTests/Verifiers/VisualBasicCodeFixVerifier`2+Test.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 Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Testing; +using Microsoft.CodeAnalysis.VisualBasic.Testing; + +using TestContext = Microsoft.VisualStudio.TestTools.UnitTesting.TestContext; + +namespace MSTest.Analyzers.Test; + +public static partial class VisualBasicCodeFixVerifier + where TAnalyzer : DiagnosticAnalyzer, new() + where TCodeFix : CodeFixProvider, new() +{ + public class Test : VisualBasicCodeFixTest + { + public Test() + { +#if NET462 + ReferenceAssemblies = ReferenceAssemblies.NetFramework.Net462.Default; + TestState.AdditionalReferences.Add(MetadataReference.CreateFromFile(typeof(ValueTask<>).Assembly.Location)); + TestState.AdditionalReferences.Add(MetadataReference.CreateFromFile(typeof(IAsyncDisposable).Assembly.Location)); +#else + ReferenceAssemblies = ReferenceAssemblies.Net.Net60; +#endif + TestState.AdditionalReferences.Add(MetadataReference.CreateFromFile(typeof(ParallelizeAttribute).Assembly.Location)); + TestState.AdditionalReferences.Add(MetadataReference.CreateFromFile(typeof(TestContext).Assembly.Location)); + } + } +} diff --git a/test/UnitTests/MSTest.Analyzers.UnitTests/Verifiers/VisualBasicCodeFixVerifier`2.cs b/test/UnitTests/MSTest.Analyzers.UnitTests/Verifiers/VisualBasicCodeFixVerifier`2.cs new file mode 100644 index 0000000000..430eada725 --- /dev/null +++ b/test/UnitTests/MSTest.Analyzers.UnitTests/Verifiers/VisualBasicCodeFixVerifier`2.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 Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Testing; +using Microsoft.CodeAnalysis.VisualBasic.Testing; + +namespace MSTest.Analyzers.Test; + +public static partial class VisualBasicCodeFixVerifier + where TAnalyzer : DiagnosticAnalyzer, new() + where TCodeFix : CodeFixProvider, new() +{ + /// + public static DiagnosticResult Diagnostic() + => VisualBasicCodeFixVerifier.Diagnostic(); + + /// + public static DiagnosticResult DiagnosticIgnoringAdditionalLocations() + => VisualBasicCodeFixVerifier.Diagnostic().WithOptions(DiagnosticOptions.IgnoreAdditionalLocations); + + /// + public static DiagnosticResult Diagnostic(string diagnosticId) + => VisualBasicCodeFixVerifier.Diagnostic(diagnosticId); + + /// + public static DiagnosticResult Diagnostic(DiagnosticDescriptor descriptor) + => VisualBasicCodeFixVerifier.Diagnostic(descriptor); + + /// + public static async Task VerifyAnalyzerAsync([StringSyntax("VB-test")] string source, params DiagnosticResult[] expected) + { + var test = new Test + { + TestCode = source, + }; + + test.ExpectedDiagnostics.AddRange(expected); + await test.RunAsync(CancellationToken.None); + } + + /// + public static async Task VerifyCodeFixAsync([StringSyntax("VB-test")] string source, [StringSyntax("VB-test")] string fixedSource) + => await VerifyCodeFixAsync(source, DiagnosticResult.EmptyDiagnosticResults, fixedSource); + + /// + public static async Task VerifyCodeFixAsync([StringSyntax("VB-test")] string source, DiagnosticResult expected, [StringSyntax("VB-test")] string fixedSource) + => await VerifyCodeFixAsync(source, [expected], fixedSource); + + /// + public static async Task VerifyCodeFixAsync([StringSyntax("VB-test")] string source, DiagnosticResult[] expected, [StringSyntax("VB-test")] string fixedSource) + { + var test = new Test + { + TestCode = source, + FixedCode = fixedSource, + }; + + test.ExpectedDiagnostics.AddRange(expected); + await test.RunAsync(CancellationToken.None); + } +} diff --git a/test/UnitTests/MSTest.Engine.UnitTests/Adapter_ExecuteRequestAsyncTests.cs b/test/UnitTests/MSTest.Engine.UnitTests/Adapter_ExecuteRequestAsyncTests.cs index 5100e968e7..73bc6395ab 100644 --- a/test/UnitTests/MSTest.Engine.UnitTests/Adapter_ExecuteRequestAsyncTests.cs +++ b/test/UnitTests/MSTest.Engine.UnitTests/Adapter_ExecuteRequestAsyncTests.cs @@ -27,19 +27,17 @@ public async Task ExecutableNode_ThatDoesNotThrow_ShouldReportPassed() }; var services = new Services(); - var adapter = new TestFramework(new(), new[] { new FactoryTestNodesBuilder(() => new[] { testNode }) }, new(), + var adapter = new TestFramework(new(), [new FactoryTestNodesBuilder(() => [testNode])], new(), services.ServiceProvider.GetSystemClock(), services.ServiceProvider.GetTask(), services.ServiceProvider.GetConfiguration(), new Platform.Capabilities.TestFramework.TestFrameworkCapabilities()); CancellationToken cancellationToken = CancellationToken.None; // Act -#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. await adapter.ExecuteRequestAsync(new( new RunTestExecutionRequest(new(new("id"), new ClientInfo(string.Empty, string.Empty))), services.ServiceProvider.GetRequiredService(), new SemaphoreSlimRequestCompleteNotifier(new SemaphoreSlim(1)), cancellationToken)); -#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. // Assert IEnumerable nodeStateChanges = services.MessageBus.Messages.OfType(); @@ -62,18 +60,16 @@ public async Task ExecutableNode_ThatThrows_ShouldReportError() var services = new Services(); var fakeClock = (FakeClock)services.ServiceProvider.GetService(typeof(FakeClock))!; - var adapter = new TestFramework(new(), new[] { new FactoryTestNodesBuilder(() => new[] { testNode }) }, new(), + var adapter = new TestFramework(new(), [new FactoryTestNodesBuilder(() => [testNode])], new(), services.ServiceProvider.GetSystemClock(), services.ServiceProvider.GetTask(), services.ServiceProvider.GetConfiguration(), new Platform.Capabilities.TestFramework.TestFrameworkCapabilities()); CancellationToken cancellationToken = CancellationToken.None; // Act -#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. await adapter.ExecuteRequestAsync(new( new RunTestExecutionRequest(new(new("id"), new ClientInfo(string.Empty, string.Empty))), services.ServiceProvider.GetRequiredService(), new SemaphoreSlimRequestCompleteNotifier(new SemaphoreSlim(1)), cancellationToken)); -#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. // Assert IEnumerable nodeStateChanges = services.MessageBus.Messages.OfType(); @@ -81,19 +77,18 @@ await adapter.ExecuteRequestAsync(new( Platform.Extensions.Messages.TestNode lastNode = nodeStateChanges.Last().TestNode; _ = lastNode.Properties.Single(); Assert.AreEqual("Oh no!", lastNode.Properties.Single().Exception!.Message); - Assert.IsTrue( - lastNode.Properties.Single().Exception!.StackTrace! - .Contains(nameof(ExecutableNode_ThatThrows_ShouldReportError)), "lastNode properties should contain the name of the test"); + Assert.Contains( + nameof(ExecutableNode_ThatThrows_ShouldReportError), lastNode.Properties.Single().Exception!.StackTrace!, "lastNode properties should contain the name of the test"); TimingProperty timingProperty = lastNode.Properties.Single(); Assert.AreEqual(fakeClock.UsedTimes[0], timingProperty.GlobalTiming.StartTime); Assert.IsTrue(timingProperty.GlobalTiming.StartTime <= timingProperty.GlobalTiming.EndTime, "start time is before (or the same as) stop time"); Assert.AreEqual(fakeClock.UsedTimes[1], timingProperty.GlobalTiming.EndTime); - Assert.IsTrue(timingProperty.GlobalTiming.Duration.TotalMilliseconds > 0, $"duration should be greater than 0"); + Assert.IsGreaterThan(0, timingProperty.GlobalTiming.Duration.TotalMilliseconds, $"duration should be greater than 0"); } private sealed class FakeClock : IClock { - public List UsedTimes { get; } = new(); + public List UsedTimes { get; } = []; public DateTimeOffset UtcNow { @@ -115,9 +110,7 @@ public Services() ServiceProvider.AddService(new LoggerFactory()); ServiceProvider.AddService(new FakeClock()); ServiceProvider.AddService(new SystemTask()); -#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. - ServiceProvider.AddService(new AggregatedConfiguration(Array.Empty(), new CurrentTestApplicationModuleInfo(new SystemEnvironment(), new SystemProcessHandler()), new SystemFileSystem(), new(null, [], []))); -#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. + ServiceProvider.AddService(new AggregatedConfiguration([], new CurrentTestApplicationModuleInfo(new SystemEnvironment(), new SystemProcessHandler()), new SystemFileSystem(), new(null, [], []))); } public MessageBus MessageBus { get; } @@ -127,7 +120,7 @@ public Services() private sealed class MessageBus : IMessageBus { - public List Messages { get; } = new(); + public List Messages { get; } = []; public Task PublishAsync(IDataProducer dataProducer, IData data) { diff --git a/test/UnitTests/MSTest.Engine.UnitTests/BFSTestNodeVisitorTests.cs b/test/UnitTests/MSTest.Engine.UnitTests/BFSTestNodeVisitorTests.cs index 847ecdf276..0676c3dd5f 100644 --- a/test/UnitTests/MSTest.Engine.UnitTests/BFSTestNodeVisitorTests.cs +++ b/test/UnitTests/MSTest.Engine.UnitTests/BFSTestNodeVisitorTests.cs @@ -4,7 +4,6 @@ using Microsoft.Testing.Platform.Extensions.Messages; using Microsoft.Testing.Platform.Requests; -#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.Framework.UnitTests; [TestClass] @@ -18,21 +17,21 @@ public async Task Visit_WhenFilterDoesNotUseEncodedSlash_NodeIsNotIncluded() { StableUid = "ID1", DisplayName = "A", - Tests = new[] - { + Tests = + [ new TestNode { StableUid = "ID2", DisplayName = "B/C", }, - }, + ], }; var filter = new TreeNodeFilter("/A/B/C"); var visitor = new BFSTestNodeVisitor(new[] { rootNode }, filter, null!); // Act - List includedTestNodes = new(); + List includedTestNodes = []; await visitor.VisitAsync((testNode, _) => { includedTestNodes.Add(testNode); @@ -40,7 +39,7 @@ await visitor.VisitAsync((testNode, _) => }); // Assert - Assert.AreEqual(1, includedTestNodes.Count); + Assert.HasCount(1, includedTestNodes); Assert.AreEqual("ID1", includedTestNodes[0].StableUid); } @@ -55,21 +54,21 @@ public async Task Visit_WhenFilterUsesEncodedEntry_NodeIsIncluded(string nodeSpe { StableUid = "ID1", DisplayName = "A", - Tests = new[] - { + Tests = + [ new TestNode { StableUid = "ID2", DisplayName = "B" + nodeSpecialString + "C", }, - }, + ], }; var filter = new TreeNodeFilter("/A/B" + filterEncodedSpecialString + "C"); var visitor = new BFSTestNodeVisitor(new[] { rootNode }, filter, null!); // Act - List includedTestNodes = new(); + List includedTestNodes = []; await visitor.VisitAsync((testNode, _) => { includedTestNodes.Add(testNode); @@ -77,7 +76,7 @@ await visitor.VisitAsync((testNode, _) => }); // Assert - Assert.AreEqual(2, includedTestNodes.Count); + Assert.HasCount(2, includedTestNodes); Assert.AreEqual("ID1", includedTestNodes[0].StableUid); Assert.AreEqual("ID2", includedTestNodes[1].StableUid); } @@ -107,7 +106,7 @@ public async Task Visit_WhenNodeIsNotParameterizedNode_DoesNotExpand(string nonP var visitor = new BFSTestNodeVisitor(new[] { rootNode }, new NopFilter(), null!); // Act - List includedTestNodes = new(); + List includedTestNodes = []; await visitor.VisitAsync((testNode, _) => { includedTestNodes.Add(testNode); @@ -115,7 +114,7 @@ await visitor.VisitAsync((testNode, _) => }); // Assert - Assert.AreEqual(1, includedTestNodes.Count); + Assert.HasCount(1, includedTestNodes); Assert.AreEqual("ID1", includedTestNodes[0].StableUid); } @@ -129,7 +128,7 @@ public async Task Visit_WhenNodeIsParameterizedNodeAndPropertyIsAbsentOrTrue_Exp var visitor = new BFSTestNodeVisitor(new[] { rootNode }, new NopFilter(), new TestArgumentsManager()); // Act - List<(TestNode Node, TestNodeUid? ParentNodeUid)> includedTestNodes = new(); + List<(TestNode Node, TestNodeUid? ParentNodeUid)> includedTestNodes = []; await visitor.VisitAsync((testNode, parentNodeUid) => { includedTestNodes.Add((testNode, parentNodeUid)); @@ -137,7 +136,7 @@ await visitor.VisitAsync((testNode, parentNodeUid) => }); // Assert - Assert.AreEqual(3, includedTestNodes.Count); + Assert.HasCount(3, includedTestNodes); Assert.AreEqual("ID1", includedTestNodes[0].Node.StableUid); Assert.IsNull(includedTestNodes[0].ParentNodeUid); @@ -158,7 +157,7 @@ public async Task Visit_WhenNodeIsParameterizedNodeAndDoesNotAllowExpansion_Does var visitor = new BFSTestNodeVisitor(new[] { rootNode }, new NopFilter(), new TestArgumentsManager()); // Act - List<(TestNode Node, TestNodeUid? ParentNodeUid)> includedTestNodes = new(); + List<(TestNode Node, TestNodeUid? ParentNodeUid)> includedTestNodes = []; await visitor.VisitAsync((testNode, parentNodeUid) => { includedTestNodes.Add((testNode, parentNodeUid)); @@ -166,7 +165,7 @@ await visitor.VisitAsync((testNode, parentNodeUid) => }); // Assert - Assert.AreEqual(1, includedTestNodes.Count); + Assert.HasCount(1, includedTestNodes); } [TestMethod] @@ -177,14 +176,14 @@ public async Task Visit_WithModuleNamespaceClassMethodLevelAndExpansion_Discover { StableUid = "MyModule", DisplayName = "MyModule", - Tests = new[] - { + Tests = + [ new TestNode { StableUid = "MyNamespace", DisplayName = "MyNamespace", - Tests = new[] - { + Tests = + [ new TestNode { StableUid = "MyType", @@ -200,14 +199,14 @@ public async Task Visit_WithModuleNamespaceClassMethodLevelAndExpansion_Discover }, }, }, - }, + ], }, - }, + ], }; var visitor = new BFSTestNodeVisitor(new[] { rootNode }, new NopFilter(), new TestArgumentsManager()); // Act - List<(TestNode Node, TestNodeUid? ParentNodeUid)> includedTestNodes = new(); + List<(TestNode Node, TestNodeUid? ParentNodeUid)> includedTestNodes = []; await visitor.VisitAsync((testNode, parentNodeUid) => { includedTestNodes.Add((testNode, parentNodeUid)); @@ -215,7 +214,7 @@ await visitor.VisitAsync((testNode, parentNodeUid) => }); // Assert - Assert.AreEqual(7, includedTestNodes.Count); + Assert.HasCount(7, includedTestNodes); Assert.AreEqual("MyModule", includedTestNodes[0].Node.StableUid); Assert.IsNull(includedTestNodes[0].ParentNodeUid); @@ -267,13 +266,13 @@ private static TestNode CreateParameterizedTestNode(string parameterizedTestNode static IEnumerable GetArguments() => new byte[] { 0, 1 }; static IProperty[] GetProperties(bool? hasExpansionProperty) => hasExpansionProperty.HasValue - ? new IProperty[1] - { + ? + [ new FrameworkEngineMetadataProperty { PreventArgumentsExpansion = hasExpansionProperty.Value, }, - } - : Array.Empty(); + ] + : []; } } diff --git a/test/UnitTests/MSTest.Engine.UnitTests/DynamicDataNameProviderTests.cs b/test/UnitTests/MSTest.Engine.UnitTests/DynamicDataNameProviderTests.cs index f511c2ff51..f6c7004659 100644 --- a/test/UnitTests/MSTest.Engine.UnitTests/DynamicDataNameProviderTests.cs +++ b/test/UnitTests/MSTest.Engine.UnitTests/DynamicDataNameProviderTests.cs @@ -15,7 +15,7 @@ public void NullTranslatesToNullString() // you will get empty string while with the call you will get "null,a". // // check that this is still true: - string fragment = DynamicDataNameProvider.GetUidFragment(["parameter1", "parameter2"], new object?[] { null, "a" }, 0); + string fragment = DynamicDataNameProvider.GetUidFragment(["parameter1", "parameter2"], [null, "a"], 0); Assert.AreEqual("(parameter1: null, parameter2: a)[0]", fragment); } @@ -28,7 +28,7 @@ public void ParameterMismatchShowsDataInMessage() // you will get empty string while with the call you will get "null,a". // // check that this is still true: - ArgumentException exception = Assert.ThrowsException(() => DynamicDataNameProvider.GetUidFragment(["parameter1"], new object?[] { null, "a" }, 0)); + ArgumentException exception = Assert.ThrowsExactly(() => DynamicDataNameProvider.GetUidFragment(["parameter1"], [null, "a"], 0)); Assert.AreEqual("Parameter count mismatch. The provided data (null, a) have 2 items, but there are 1 parameters.", exception.Message); } } diff --git a/test/UnitTests/MSTest.Engine.UnitTests/DynamicDataTests.cs b/test/UnitTests/MSTest.Engine.UnitTests/DynamicDataTests.cs index ea608a39d3..7c4ba68622 100644 --- a/test/UnitTests/MSTest.Engine.UnitTests/DynamicDataTests.cs +++ b/test/UnitTests/MSTest.Engine.UnitTests/DynamicDataTests.cs @@ -10,18 +10,18 @@ namespace Microsoft.Testing.Framework.UnitTests; public class DynamicDataTests { public static IEnumerable IntDataProperty - => new[] - { - new object[] { 1, 2 }, - new object[] { 2, 3 }, - }; + => + [ + [1, 2], + [2, 3] + ]; [DynamicData(nameof(IntDataProperty))] [TestMethod] public void DynamicDataWithIntProperty(int expected, int actualPlus1) => Assert.AreEqual(expected, actualPlus1 - 1); - [DynamicData(nameof(IntDataProperty), DynamicDataSourceType.Property)] + [DynamicData(nameof(IntDataProperty))] [TestMethod] public void DynamicDataWithIntPropertyAndExplicitSourceType(int expected, int actualPlus1) => Assert.AreEqual(expected, actualPlus1 - 1); @@ -31,24 +31,24 @@ public void DynamicDataWithIntPropertyAndExplicitSourceType(int expected, int ac public void DynamicDataWithIntMethod(int expected, int actualPlus1) => Assert.AreEqual(expected, actualPlus1 - 1); - [DynamicData(nameof(IntDataMethod), DynamicDataSourceType.Method)] + [DynamicData(nameof(IntDataMethod))] [TestMethod] public void DynamicDataWithIntMethodAndExplicitSourceType(int expected, int actualPlus1) => Assert.AreEqual(expected, actualPlus1 - 1); public static IEnumerable IntDataMethod() - => new[] - { - new object[] { 1, 2 }, - new object[] { 2, 3 }, - }; + => + [ + [1, 2], + [2, 3] + ]; [DynamicData(nameof(IntDataProperty), typeof(DataClass))] [TestMethod] public void DynamicDataWithIntPropertyOnSeparateClass(int expected, int actualPlus2) => Assert.AreEqual(expected, actualPlus2 - 2); - [DynamicData(nameof(IntDataMethod), typeof(DataClass), DynamicDataSourceType.Method)] + [DynamicData(nameof(IntDataMethod), typeof(DataClass))] [TestMethod] public void DynamicDataWithIntMethodOnSeparateClass(int expected, int actualPlus2) => Assert.AreEqual(expected, actualPlus2 - 2); @@ -60,41 +60,41 @@ public void DynamicDataWithUserProperty(User _, User _2) } public static IEnumerable UserDataProperty - => new[] - { - new object[] { new User("Jakub"), new User("Amaury") }, - new object[] { new User("Marco"), new User("Pavel") }, - }; + => + [ + [new User("Jakub"), new User("Amaury")], + [new User("Marco"), new User("Pavel")] + ]; - [DynamicData(nameof(UserDataMethod), DynamicDataSourceType.Method)] + [DynamicData(nameof(UserDataMethod))] [TestMethod] public void DynamicDataWithUserMethod(User _, User _2) { } public static IEnumerable UserDataMethod() - => new[] - { - new object[] { new User("Jakub"), new User("Amaury") }, - new object[] { new User("Marco"), new User("Pavel") }, - }; + => + [ + [new User("Jakub"), new User("Amaury")], + [new User("Marco"), new User("Pavel")] + ]; } public class DataClass { public static IEnumerable IntDataProperty - => new[] - { - new object[] { 1, 3 }, - new object[] { 2, 4 }, - }; + => + [ + [1, 3], + [2, 4] + ]; public static IEnumerable IntDataMethod() - => new[] - { - new object[] { 1, 3 }, - new object[] { 2, 4 }, - }; + => + [ + [1, 3], + [2, 4] + ]; } public class User diff --git a/test/UnitTests/MSTest.Engine.UnitTests/TestBase.cs b/test/UnitTests/MSTest.Engine.UnitTests/TestBase.cs index 9c09f265b5..0e748f47ce 100644 --- a/test/UnitTests/MSTest.Engine.UnitTests/TestBase.cs +++ b/test/UnitTests/MSTest.Engine.UnitTests/TestBase.cs @@ -6,6 +6,4 @@ namespace Microsoft.Testing.Framework.UnitTests; /// /// Empty test base, because TestInfrastructure project depends on Testing.Framework, and we cannot use that. /// -public abstract class TestBase -{ -} +public abstract class TestBase; diff --git a/test/UnitTests/MSTest.SelfRealExamples.UnitTests/MSTest.SelfRealExamples.UnitTests.csproj b/test/UnitTests/MSTest.SelfRealExamples.UnitTests/MSTest.SelfRealExamples.UnitTests.csproj new file mode 100644 index 0000000000..f8e01b977b --- /dev/null +++ b/test/UnitTests/MSTest.SelfRealExamples.UnitTests/MSTest.SelfRealExamples.UnitTests.csproj @@ -0,0 +1,29 @@ + + + + Exe + net472;net9.0 + enable + Exe + true + + + + + + + + + + + + + + + + + + + + + diff --git a/test/UnitTests/MSTest.SelfRealExamples.UnitTests/ParameterizedTestSerializationIssue2390.cs b/test/UnitTests/MSTest.SelfRealExamples.UnitTests/ParameterizedTestSerializationIssue2390.cs new file mode 100644 index 0000000000..073cc54902 --- /dev/null +++ b/test/UnitTests/MSTest.SelfRealExamples.UnitTests/ParameterizedTestSerializationIssue2390.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.VisualStudio.TestTools.UnitTesting; + +namespace MSTest.SelfRealExamples.UnitTests; + +// Test for https://github.com/microsoft/testfx/issues/2390 +[TestClass] +public class ParameterizedTestSerializationIssue2390 +{ + [TestMethod] + [DataRow((byte)0, new object[] { (byte)0 })] + [DataRow((short)0, new object[] { (short)0 })] + [DataRow(0L, new object[] { 0L })] + public void CheckNestedInputTypes(object expected, object nested) + { + object[] array = (object[])nested; + object actual = Assert.ContainsSingle(array); +#if NETFRAMEWORK + // Buggy behavior, because of app domains. + Assert.AreEqual(typeof(int), actual.GetType()); +#else + Assert.AreEqual(expected.GetType(), actual.GetType()); + Assert.AreEqual(expected, actual); +#endif + } +} diff --git a/test/UnitTests/MSTest.SelfRealExamples.UnitTests/Program.cs b/test/UnitTests/MSTest.SelfRealExamples.UnitTests/Program.cs new file mode 100644 index 0000000000..904b1066e1 --- /dev/null +++ b/test/UnitTests/MSTest.SelfRealExamples.UnitTests/Program.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.Testing.Extensions; +using Microsoft.Testing.Platform.Builder; + +using Microsoft.VisualStudio.TestTools.UnitTesting; + +[assembly: Parallelize(Scope = Microsoft.VisualStudio.TestTools.UnitTesting.ExecutionScope.ClassLevel, Workers = 0)] + +ITestApplicationBuilder testApplicationBuilder = await TestApplication.CreateBuilderAsync(args); + +testApplicationBuilder.AddMSTest(() => [Assembly.GetEntryAssembly()!]); +testApplicationBuilder.AddTrxReportProvider(); +testApplicationBuilder.AddAppInsightsTelemetryProvider(); +testApplicationBuilder.AddCodeCoverageProvider(); +testApplicationBuilder.AddAzureDevOpsProvider(); +using ITestApplication testApplication = await testApplicationBuilder.BuildAsync(); +return await testApplication.RunAsync(); diff --git a/test/UnitTests/MSTest.SelfRealExamples.UnitTests/TestExecutionContextPropagationFromTestMethodAttributeToTestMethod.cs b/test/UnitTests/MSTest.SelfRealExamples.UnitTests/TestExecutionContextPropagationFromTestMethodAttributeToTestMethod.cs new file mode 100644 index 0000000000..274c3c5524 --- /dev/null +++ b/test/UnitTests/MSTest.SelfRealExamples.UnitTests/TestExecutionContextPropagationFromTestMethodAttributeToTestMethod.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.VisualStudio.TestTools.UnitTesting; + +namespace MSTest.SelfRealExamples.UnitTests; + +[TestClass] +public sealed class TestExecutionContextPropagationFromTestMethodAttributeToTestMethod +{ + private static readonly AsyncLocal State = new(); + + [ClassInitialize] + public static void ClassInitialize(TestContext testContext) + { + } + + [MyTestMethod] + public void TestAsyncLocalValueIsPreserved() + => Assert.AreEqual("In Execute", State.Value); + + private sealed class MyTestMethodAttribute : TestMethodAttribute + { + public override TestResult[] Execute(ITestMethod testMethod) + { + State.Value = "In Execute"; + return base.Execute(testMethod); + } + } +} diff --git a/test/UnitTests/MSTest.SourceGeneration.UnitTests/Generators/DataRowAttributeGenerationTests.cs b/test/UnitTests/MSTest.SourceGeneration.UnitTests/Generators/DataRowAttributeGenerationTests.cs index 352b28d89f..4c46d80fea 100644 --- a/test/UnitTests/MSTest.SourceGeneration.UnitTests/Generators/DataRowAttributeGenerationTests.cs +++ b/test/UnitTests/MSTest.SourceGeneration.UnitTests/Generators/DataRowAttributeGenerationTests.cs @@ -1,7 +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 FluentAssertions; +using AwesomeAssertions; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Text; @@ -13,6 +13,8 @@ namespace Microsoft.Testing.Framework.SourceGeneration.UnitTests.Generators; [TestClass] public sealed class DataRowAttributeGenerationTests : TestBase { + public TestContext TestContext { get; set; } + [TestMethod] public async Task DataRowAttribute_HandlesPrimitiveTypes() { @@ -98,7 +100,7 @@ public Task MethodWithUShort(ushort s) SyntaxTree? testClassTree = generatorResult.GeneratedTrees.FirstOrDefault(r => r.FilePath.EndsWith("TestClass.g.cs", StringComparison.OrdinalIgnoreCase)); testClassTree.Should().NotBeNull(); - SourceText testClass = await testClassTree!.GetTextAsync(); + SourceText testClass = await testClassTree!.GetTextAsync(TestContext.CancellationToken); testClass.Should().ContainSourceCode(""" GetArguments = static () => new MSTF::InternalUnsafeTestArgumentsEntry[] @@ -213,7 +215,7 @@ public Task TestMethod(string s, int i, bool b, double d) SyntaxTree? testClassTree = generatorResult.GeneratedTrees.FirstOrDefault(r => r.FilePath.EndsWith("TestClass.g.cs", StringComparison.OrdinalIgnoreCase)); testClassTree.Should().NotBeNull(); - SourceText testClass = await testClassTree!.GetTextAsync(); + SourceText testClass = await testClassTree!.GetTextAsync(TestContext.CancellationToken); testClass.Should().ContainSourceCode(""" GetArguments = static () => new MSTF::InternalUnsafeTestArgumentsEntry<(string s, int i)>[] @@ -257,7 +259,7 @@ public Task TestMethod(string a) SyntaxTree? testClassTree = generatorResult.GeneratedTrees.FirstOrDefault(r => r.FilePath.EndsWith("TestClass.g.cs", StringComparison.OrdinalIgnoreCase)); testClassTree.Should().NotBeNull(); - SourceText testClass = await testClassTree!.GetTextAsync(); + SourceText testClass = await testClassTree!.GetTextAsync(TestContext.CancellationToken); testClass.Should().ContainSourceCode(""" GetArguments = static () => new MSTF::InternalUnsafeTestArgumentsEntry[] @@ -301,7 +303,7 @@ public Task TestMethod(Type a) SyntaxTree? testClassTree = generatorResult.GeneratedTrees.FirstOrDefault(r => r.FilePath.EndsWith("TestClass.g.cs", StringComparison.OrdinalIgnoreCase)); testClassTree.Should().NotBeNull(); - SourceText testClass = await testClassTree!.GetTextAsync(); + SourceText testClass = await testClassTree!.GetTextAsync(TestContext.CancellationToken); testClass.Should().ContainSourceCode(""" GetArguments = static () => new MSTF::InternalUnsafeTestArgumentsEntry[] @@ -348,7 +350,7 @@ public Task TestMethod(MyEnum a) SyntaxTree? testClassTree = generatorResult.GeneratedTrees.FirstOrDefault(r => r.FilePath.EndsWith("TestClass.g.cs", StringComparison.OrdinalIgnoreCase)); testClassTree.Should().NotBeNull(); - SourceText testClass = await testClassTree!.GetTextAsync(); + SourceText testClass = await testClassTree!.GetTextAsync(TestContext.CancellationToken); testClass.Should().ContainSourceCode(""" GetArguments = static () => new MSTF::InternalUnsafeTestArgumentsEntry[] @@ -408,7 +410,7 @@ public Task TestMethod(MyEnum a) SyntaxTree? testClassTree = generatorResult.GeneratedTrees.FirstOrDefault(r => r.FilePath.EndsWith("TestClass.g.cs", StringComparison.OrdinalIgnoreCase)); testClassTree.Should().NotBeNull(); - SourceText testClass = await testClassTree!.GetTextAsync(); + SourceText testClass = await testClassTree!.GetTextAsync(TestContext.CancellationToken); testClass.Should().ContainSourceCode(""" GetArguments = static () => new MSTF::InternalUnsafeTestArgumentsEntry[] @@ -462,7 +464,7 @@ public Task TestMethod(ConflictingNamespace.MyEnum a) SyntaxTree? testClassTree = generatorResult.GeneratedTrees.FirstOrDefault(r => r.FilePath.EndsWith("TestClass.g.cs", StringComparison.OrdinalIgnoreCase)); testClassTree.Should().NotBeNull(); - SourceText testClass = await testClassTree!.GetTextAsync(); + SourceText testClass = await testClassTree!.GetTextAsync(TestContext.CancellationToken); testClass.Should().ContainSourceCode(""" GetArguments = static () => new MSTF::InternalUnsafeTestArgumentsEntry[] @@ -511,7 +513,7 @@ public Task TestMethod1(string s, object a) SyntaxTree? testClassTree = generatorResult.GeneratedTrees.FirstOrDefault(r => r.FilePath.EndsWith("TestClass.g.cs", StringComparison.OrdinalIgnoreCase)); testClassTree.Should().NotBeNull(); - SourceText testClass = await testClassTree!.GetTextAsync(); + SourceText testClass = await testClassTree!.GetTextAsync(TestContext.CancellationToken); testClass.Should().ContainSourceCode(""" GetArguments = static () => new MSTF::InternalUnsafeTestArgumentsEntry[] @@ -565,7 +567,7 @@ public Task TestMethod2(string a, string b) SyntaxTree? testClassTree = generatorResult.GeneratedTrees.FirstOrDefault(r => r.FilePath.EndsWith("TestClass.g.cs", StringComparison.OrdinalIgnoreCase)); testClassTree.Should().NotBeNull(); - SourceText testClass = await testClassTree!.GetTextAsync(); + SourceText testClass = await testClassTree!.GetTextAsync(TestContext.CancellationToken); testClass.Should().ContainSourceCode(""" // The test method is parameterized but no argument was specified. @@ -646,7 +648,7 @@ public Task OneObjectArray(object[] args) SyntaxTree? testClassTree = generatorResult.GeneratedTrees.FirstOrDefault(r => r.FilePath.EndsWith("TestClass.g.cs", StringComparison.OrdinalIgnoreCase)); testClassTree.Should().NotBeNull(); - SourceText testClass = await testClassTree!.GetTextAsync(); + SourceText testClass = await testClassTree!.GetTextAsync(TestContext.CancellationToken); testClass.Should().ContainSourceCode(""" GetArguments = static () => new MSTF::InternalUnsafeTestArgumentsEntry[] @@ -683,7 +685,7 @@ public Task OneIntArray(int[] args) SyntaxTree? testClassTree = generatorResult.GeneratedTrees.FirstOrDefault(r => r.FilePath.EndsWith("TestClass.g.cs", StringComparison.OrdinalIgnoreCase)); testClassTree.Should().NotBeNull(); - SourceText testClass = await testClassTree!.GetTextAsync(); + SourceText testClass = await testClassTree!.GetTextAsync(TestContext.CancellationToken); testClass.Should().ContainSourceCode(""" GetArguments = static () => new MSTF::InternalUnsafeTestArgumentsEntry[] @@ -720,7 +722,7 @@ public Task OneStringArray(string[] args) SyntaxTree? testClassTree = generatorResult.GeneratedTrees.FirstOrDefault(r => r.FilePath.EndsWith("TestClass.g.cs", StringComparison.OrdinalIgnoreCase)); testClassTree.Should().NotBeNull(); - SourceText testClass = await testClassTree!.GetTextAsync(); + SourceText testClass = await testClassTree!.GetTextAsync(TestContext.CancellationToken); testClass.Should().ContainSourceCode(""" GetArguments = static () => new MSTF::InternalUnsafeTestArgumentsEntry[] @@ -756,7 +758,7 @@ public Task OneParamsObjectArray2(object[] args) SyntaxTree? testClassTree = generatorResult.GeneratedTrees.FirstOrDefault(r => r.FilePath.EndsWith("TestClass.g.cs", StringComparison.OrdinalIgnoreCase)); testClassTree.Should().NotBeNull(); - SourceText testClass = await testClassTree!.GetTextAsync(); + SourceText testClass = await testClassTree!.GetTextAsync(TestContext.CancellationToken); testClass.Should().ContainSourceCode(""" GetArguments = static () => new MSTF::InternalUnsafeTestArgumentsEntry[] @@ -794,7 +796,7 @@ public Task OneIntArray(object[] args) SyntaxTree? testClassTree = generatorResult.GeneratedTrees.FirstOrDefault(r => r.FilePath.EndsWith("TestClass.g.cs", StringComparison.OrdinalIgnoreCase)); testClassTree.Should().NotBeNull(); - SourceText testClass = await testClassTree!.GetTextAsync(); + SourceText testClass = await testClassTree!.GetTextAsync(TestContext.CancellationToken); testClass.Should().ContainSourceCode(""" GetArguments = static () => new MSTF::InternalUnsafeTestArgumentsEntry[] @@ -831,7 +833,7 @@ public Task TwoObjectArrays(object[] args, object[] args2) SyntaxTree? testClassTree = generatorResult.GeneratedTrees.FirstOrDefault(r => r.FilePath.EndsWith("TestClass.g.cs", StringComparison.OrdinalIgnoreCase)); testClassTree.Should().NotBeNull(); - SourceText testClass = await testClassTree!.GetTextAsync(); + SourceText testClass = await testClassTree!.GetTextAsync(TestContext.CancellationToken); testClass.Should().ContainSourceCode(""" GetArguments = static () => new MSTF::InternalUnsafeTestArgumentsEntry<(object[] args, object[] args2)>[] diff --git a/test/UnitTests/MSTest.SourceGeneration.UnitTests/Generators/DynamicDataAttributeTests.cs b/test/UnitTests/MSTest.SourceGeneration.UnitTests/Generators/DynamicDataAttributeTests.cs index 1016eaf683..7ec15f78af 100644 --- a/test/UnitTests/MSTest.SourceGeneration.UnitTests/Generators/DynamicDataAttributeTests.cs +++ b/test/UnitTests/MSTest.SourceGeneration.UnitTests/Generators/DynamicDataAttributeTests.cs @@ -1,7 +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 FluentAssertions; +using AwesomeAssertions; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Text; @@ -13,6 +13,8 @@ namespace Microsoft.Testing.Framework.SourceGeneration.UnitTests.Generators; [TestClass] public sealed class DynamicDataAttributeGenerationTests : TestBase { + public TestContext TestContext { get; set; } + [TestMethod] public async Task DynamicDataAttribute_TakesDataFromProperty() { @@ -47,7 +49,7 @@ public void TestMethod(int expected, int actualPlus1) SyntaxTree? testClassTree = generatorResult.GeneratedTrees.FirstOrDefault(r => r.FilePath.EndsWith("TestClass.g.cs", StringComparison.OrdinalIgnoreCase)); testClassTree.Should().NotBeNull(); - SourceText testClass = await testClassTree!.GetTextAsync(); + SourceText testClass = await testClassTree!.GetTextAsync(TestContext.CancellationToken); testClass.Should().ContainSourceCode(""" GetArguments = static () => { @@ -99,7 +101,7 @@ public static IEnumerable Data() => new[] SyntaxTree? testClassTree = generatorResult.GeneratedTrees.FirstOrDefault(r => r.FilePath.EndsWith("TestClass.g.cs", StringComparison.OrdinalIgnoreCase)); testClassTree.Should().NotBeNull(); - SourceText testClass = await testClassTree!.GetTextAsync(); + SourceText testClass = await testClassTree!.GetTextAsync(TestContext.CancellationToken); testClass.Should().ContainSourceCode(""" GetArguments = static () => { diff --git a/test/UnitTests/MSTest.SourceGeneration.UnitTests/Generators/IgnoreAttributeGenerationTests.cs b/test/UnitTests/MSTest.SourceGeneration.UnitTests/Generators/IgnoreAttributeGenerationTests.cs index 26e69d96c2..aacf0c8eee 100644 --- a/test/UnitTests/MSTest.SourceGeneration.UnitTests/Generators/IgnoreAttributeGenerationTests.cs +++ b/test/UnitTests/MSTest.SourceGeneration.UnitTests/Generators/IgnoreAttributeGenerationTests.cs @@ -1,7 +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 FluentAssertions; +using AwesomeAssertions; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Text; @@ -13,6 +13,8 @@ namespace Microsoft.Testing.Framework.SourceGeneration.UnitTests.Generators; [TestClass] public sealed class IgnoreAttributeGenerationTests : TestBase { + public TestContext TestContext { get; set; } + [TestMethod] public async Task IgnoreAttribute_OnMethodExcludesTheMethodFromCompilation() { @@ -62,7 +64,7 @@ public void IgnoredVoidMethodWithReason() { } SyntaxTree? testClassTree = generatorResult.GeneratedTrees.FirstOrDefault(r => r.FilePath.EndsWith("TestClass.g.cs", StringComparison.OrdinalIgnoreCase)); testClassTree.Should().NotBeNull(); - SourceText testClass = await testClassTree!.GetTextAsync(); + SourceText testClass = await testClassTree!.GetTextAsync(TestContext.CancellationToken); testClass.Should().ContainSourceCode("""StableUid = "TestAssembly.MyNamespace.TestClass.TestMethod1()","""); diff --git a/test/UnitTests/MSTest.SourceGeneration.UnitTests/Generators/StaticMethodGenerationTests.cs b/test/UnitTests/MSTest.SourceGeneration.UnitTests/Generators/StaticMethodGenerationTests.cs index 8a5a7de839..42107642a3 100644 --- a/test/UnitTests/MSTest.SourceGeneration.UnitTests/Generators/StaticMethodGenerationTests.cs +++ b/test/UnitTests/MSTest.SourceGeneration.UnitTests/Generators/StaticMethodGenerationTests.cs @@ -1,7 +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 FluentAssertions; +using AwesomeAssertions; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Text; @@ -13,6 +13,8 @@ namespace Microsoft.Testing.Framework.SourceGeneration.UnitTests.Generators; [TestClass] public sealed class StaticMethodGenerationTests { + public TestContext TestContext { get; set; } + [TestMethod] public async Task StaticMethods_StaticMethodsWontGenerateTests() { @@ -46,7 +48,7 @@ public static void StaticTestMethod() { } SyntaxTree? testClassTree = generatorResult.GeneratedTrees.FirstOrDefault(r => r.FilePath.EndsWith("TestClass.g.cs", StringComparison.OrdinalIgnoreCase)); testClassTree.Should().NotBeNull(); - SourceText testClass = await testClassTree!.GetTextAsync(); + SourceText testClass = await testClassTree!.GetTextAsync(TestContext.CancellationToken); testClass.Should().ContainSourceCode("""StableUid = "TestAssembly.MyNamespace.TestClass.TestMethod1()","""); diff --git a/test/UnitTests/MSTest.SourceGeneration.UnitTests/Generators/TestNodesGeneratorTests.cs b/test/UnitTests/MSTest.SourceGeneration.UnitTests/Generators/TestNodesGeneratorTests.cs index 7695466e03..6ceb1a9ed4 100644 --- a/test/UnitTests/MSTest.SourceGeneration.UnitTests/Generators/TestNodesGeneratorTests.cs +++ b/test/UnitTests/MSTest.SourceGeneration.UnitTests/Generators/TestNodesGeneratorTests.cs @@ -1,7 +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 FluentAssertions; +using AwesomeAssertions; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Text; @@ -13,6 +13,8 @@ namespace Microsoft.Testing.Framework.SourceGeneration.UnitTests.Generators; [TestClass] public sealed class TestNodesGeneratorTests : TestBase { + public TestContext TestContext { get; set; } + [DataRow("class", "public")] [DataRow("class", "internal")] [DataRow("record", "public")] @@ -44,7 +46,7 @@ public Task TestMethod() generatorResult.AssertSuccessfulGeneration(); generatorResult.GeneratedTrees.Should().HaveCount(3); - SourceText myTypeSource = await generatorResult.RunResult.GeneratedTrees[0].GetTextAsync(); + SourceText myTypeSource = await generatorResult.RunResult.GeneratedTrees[0].GetTextAsync(TestContext.CancellationToken); myTypeSource.Should().ContainSourceCode(""" public static readonly MSTF::TestNode TestNode = new MSTF::TestNode { @@ -89,7 +91,7 @@ public Task TestMethod() }; """); - SourceText rootSource = await generatorResult.RunResult.GeneratedTrees[1].GetTextAsync(); + SourceText rootSource = await generatorResult.RunResult.GeneratedTrees[1].GetTextAsync(TestContext.CancellationToken); rootSource.Should().ContainSourceCode(""" MSTF::TestNode root = new MSTF::TestNode { @@ -223,7 +225,7 @@ public Task TestMethod() generatorResult.AssertSuccessfulGeneration(); generatorResult.GeneratedTrees.Should().HaveCount(3); - SourceText myTypeSource = await generatorResult.RunResult.GeneratedTrees[0].GetTextAsync(); + SourceText myTypeSource = await generatorResult.RunResult.GeneratedTrees[0].GetTextAsync(TestContext.CancellationToken); myTypeSource.Should().ContainSourceCode(""" public static readonly MSTF::TestNode TestNode = new MSTF::TestNode { @@ -330,7 +332,7 @@ public Task TestMethod() generatorResult.AssertSuccessfulGeneration(); generatorResult.GeneratedTrees.Should().HaveCount(4); - SourceText myBaseClassSource = await generatorResult.GeneratedTrees[0].GetTextAsync(); + SourceText myBaseClassSource = await generatorResult.GeneratedTrees[0].GetTextAsync(TestContext.CancellationToken); myBaseClassSource.Should().ContainSourceCode(""" public static readonly MSTF::TestNode TestNode = new MSTF::TestNode { @@ -375,7 +377,7 @@ public Task TestMethod() }; """); - SourceText myTypeSource = await generatorResult.GeneratedTrees[1].GetTextAsync(); + SourceText myTypeSource = await generatorResult.GeneratedTrees[1].GetTextAsync(TestContext.CancellationToken); myTypeSource.Should().ContainSourceCode(""" public static readonly MSTF::TestNode TestNode = new MSTF::TestNode { @@ -488,7 +490,7 @@ public Task TestMethod() generatorResult.AssertSuccessfulGeneration(); generatorResult.RunResult.GeneratedTrees.Should().HaveCount(3); - SourceText testClass = await generatorResult.GeneratedTrees[0].GetTextAsync(); + SourceText testClass = await generatorResult.GeneratedTrees[0].GetTextAsync(TestContext.CancellationToken); testClass.Should().ContainSourceCode( "public static readonly MSTF::TestNode TestNode = new MSTF::TestNode", @@ -533,7 +535,7 @@ public Task TestMethod2() SyntaxTree? testClassTree = generatorResult.GeneratedTrees.FirstOrDefault(r => r.FilePath.EndsWith("TestSubClass.g.cs", StringComparison.OrdinalIgnoreCase)); testClassTree.Should().NotBeNull(); - SourceText testClass = await testClassTree!.GetTextAsync(); + SourceText testClass = await testClassTree!.GetTextAsync(TestContext.CancellationToken); testClass.Should().ContainSourceCode(""" new MSTF::InternalUnsafeAsyncActionTestNode { @@ -589,7 +591,7 @@ public Task TestMethod() generatorResult.AssertSuccessfulGeneration(); generatorResult.RunResult.GeneratedTrees.Should().HaveCount(3); - SourceText myTypeSource = await generatorResult.GeneratedTrees[0].GetTextAsync(); + SourceText myTypeSource = await generatorResult.GeneratedTrees[0].GetTextAsync(TestContext.CancellationToken); myTypeSource.Should().ContainSourceCode(""" public static readonly MSTF::TestNode TestNode = new MSTF::TestNode { @@ -601,7 +603,7 @@ public Task TestMethod() }, """); - SourceText rootSource = await generatorResult.GeneratedTrees[1].GetTextAsync(); + SourceText rootSource = await generatorResult.GeneratedTrees[1].GetTextAsync(TestContext.CancellationToken); rootSource.Should().ContainSourceCode(""" MSTF::TestNode root = new MSTF::TestNode { @@ -626,25 +628,24 @@ public Task TestMethod() public async Task When_MultipleClassesFromSameNamespace_ItGeneratesASingleNamespaceTestNode() { GeneratorCompilationResult generatorResult = await GeneratorTester.TestGraph.CompileAndExecuteAsync( - new[] - { - $$""" - using System.Threading.Tasks; - using Microsoft.VisualStudio.TestTools.UnitTesting; - - namespace MyNamespace - { - [TestClass] - public class MyType1 - { - [TestMethod] - public Task TestMethod() - { - return Task.CompletedTask; - } - } - } - """, + [ + $$""" + using System.Threading.Tasks; + using Microsoft.VisualStudio.TestTools.UnitTesting; + + namespace MyNamespace + { + [TestClass] + public class MyType1 + { + [TestMethod] + public Task TestMethod() + { + return Task.CompletedTask; + } + } + } + """, $$""" using System.Threading.Tasks; using Microsoft.VisualStudio.TestTools.UnitTesting; @@ -661,13 +662,13 @@ public Task TestMethod() } } } - """, - }, CancellationToken.None); + """ + ], CancellationToken.None); generatorResult.AssertSuccessfulGeneration(); generatorResult.GeneratedTrees.Should().HaveCount(4); - SourceText rootSource = await generatorResult.RunResult.GeneratedTrees[2].GetTextAsync(); + SourceText rootSource = await generatorResult.RunResult.GeneratedTrees[2].GetTextAsync(TestContext.CancellationToken); rootSource.Should().ContainSourceCode(""" ColGen::List namespace1Tests = new(); namespace1Tests.Add(MyNamespace_MyType1.TestNode); @@ -729,7 +730,7 @@ public Task InitializeAsync(InitializationContext context) generatorResult.AssertSuccessfulGeneration(); generatorResult.GeneratedTrees.Should().HaveCount(2); - SourceText myTypeSource = await generatorResult.RunResult.GeneratedTrees[0].GetTextAsync(); + SourceText myTypeSource = await generatorResult.RunResult.GeneratedTrees[0].GetTextAsync(TestContext.CancellationToken); // The test node for the type should not have a test node for the InitializeAsync method. myTypeSource.Should().NotContain("StableUid = \"TestAssembly.MyNamespace.MyType.InitializeAsync\""); @@ -787,7 +788,7 @@ public Task CleanupAsync(CleanupContext context) generatorResult.AssertSuccessfulGeneration(); generatorResult.GeneratedTrees.Should().HaveCount(2); - SourceText myTypeSource = await generatorResult.RunResult.GeneratedTrees[0].GetTextAsync(); + SourceText myTypeSource = await generatorResult.RunResult.GeneratedTrees[0].GetTextAsync(TestContext.CancellationToken); // The test node for the type should not have a test node for the CleanupAsync method. myTypeSource.Should().NotContain("StableUid = \"TestAssembly.MyNamespace.MyType.CleanupAsync\""); @@ -852,7 +853,7 @@ public void Dispose() generatorResult.AssertSuccessfulGeneration(); generatorResult.GeneratedTrees.Should().HaveCount(2); - SourceText myTypeSource = await generatorResult.RunResult.GeneratedTrees[0].GetTextAsync(); + SourceText myTypeSource = await generatorResult.RunResult.GeneratedTrees[0].GetTextAsync(TestContext.CancellationToken); // The test node for the type should not have a test node for the Dispose method. myTypeSource.Should().NotContain("StableUid = \"TestAssembly.MyNamespace.MyType.Dispose\""); @@ -910,7 +911,7 @@ public ValueTask DisposeAsync() generatorResult.AssertSuccessfulGeneration(); generatorResult.GeneratedTrees.Should().HaveCount(2); - SourceText myTypeSource = await generatorResult.RunResult.GeneratedTrees[0].GetTextAsync(); + SourceText myTypeSource = await generatorResult.RunResult.GeneratedTrees[0].GetTextAsync(TestContext.CancellationToken); // The test node for the type should not have a test node for the DisposeAsync method. myTypeSource.Should().NotContain("StableUid = \"TestAssembly.MyNamespace.MyType.DisposeAsync\""); @@ -972,7 +973,7 @@ public void Dispose() generatorResult.AssertSuccessfulGeneration(); generatorResult.GeneratedTrees.Should().HaveCount(2); - SourceText myTypeSource = await generatorResult.RunResult.GeneratedTrees[0].GetTextAsync(); + SourceText myTypeSource = await generatorResult.RunResult.GeneratedTrees[0].GetTextAsync(TestContext.CancellationToken); // The body of the test node for the method should call only DisposeAsync after calling the test method. myTypeSource.Should().ContainSourceCode(""" @@ -1047,21 +1048,21 @@ public Task CleanupAsync(CleanupContext context) generatorResult.AssertSuccessfulGeneration(); generatorResult.GeneratedTrees.Should().HaveCount(4); - SourceText myClass1Source = await generatorResult.RunResult.GeneratedTrees[0].GetTextAsync(); + SourceText myClass1Source = await generatorResult.RunResult.GeneratedTrees[0].GetTextAsync(TestContext.CancellationToken); myClass1Source.Should().ContainSourceCode(""" new MSTF::InternalUnsafeAsyncActionTestNode { StableUid = "TestAssembly.MyNamespace.MyClass1.TestMethod()", """); - SourceText myClass2Source = await generatorResult.RunResult.GeneratedTrees[1].GetTextAsync(); + SourceText myClass2Source = await generatorResult.RunResult.GeneratedTrees[1].GetTextAsync(TestContext.CancellationToken); myClass2Source.Should().ContainSourceCode(""" new MSTF::InternalUnsafeAsyncActionTestNode { StableUid = "TestAssembly.MyNamespace.MyClass2.TestMethod()", """); - SourceText myClass3Source = await generatorResult.RunResult.GeneratedTrees[2].GetTextAsync(); + SourceText myClass3Source = await generatorResult.RunResult.GeneratedTrees[2].GetTextAsync(TestContext.CancellationToken); myClass3Source.Should().ContainSourceCode(""" new MSTF::InternalUnsafeAsyncActionTestNode { @@ -1117,7 +1118,7 @@ public Task TestMethod4() generatorResult.AssertFailedGeneration("*error CS0619: 'MyType.TestMethod4()' is obsolete*"); generatorResult.GeneratedTrees.Should().HaveCount(3); - SourceText myTypeSource = await generatorResult.RunResult.GeneratedTrees[0].GetTextAsync(); + SourceText myTypeSource = await generatorResult.RunResult.GeneratedTrees[0].GetTextAsync(TestContext.CancellationToken); myTypeSource.Should().ContainSourceCode(""" #pragma warning disable CS0612 // Type or member is obsolete await instance.TestMethod1(); @@ -1154,27 +1155,26 @@ public void GenerateValidNamespaceName_WithGivenAssemblyName_ReturnsExpectedName public async Task When_APartialTypeIsMarkedWithTestClass_ItGeneratesAGraphWithAssemblyNamespaceTypeAndMethods() { GeneratorCompilationResult generatorResult = await GeneratorTester.TestGraph.CompileAndExecuteAsync( - new string[] - { - $$""" - using System.Threading.Tasks; - using Microsoft.VisualStudio.TestTools.UnitTesting; - - namespace MyNamespace - { - [TestClass] - public partial class MyType - { - public MyType(int a) { } - - [TestMethod] - public Task TestMethod1() - { - return Task.CompletedTask; - } - } - } - """, + [ + $$""" + using System.Threading.Tasks; + using Microsoft.VisualStudio.TestTools.UnitTesting; + + namespace MyNamespace + { + [TestClass] + public partial class MyType + { + public MyType(int a) { } + + [TestMethod] + public Task TestMethod1() + { + return Task.CompletedTask; + } + } + } + """, $$""" using System.Threading.Tasks; using Microsoft.VisualStudio.TestTools.UnitTesting; @@ -1196,13 +1196,13 @@ public Task TestMethod2() } } } - """, - }, CancellationToken.None); + """ + ], CancellationToken.None); generatorResult.AssertSuccessfulGeneration(); generatorResult.GeneratedTrees.Should().HaveCount(3); - SourceText myTypeSource = await generatorResult.RunResult.GeneratedTrees[0].GetTextAsync(); + SourceText myTypeSource = await generatorResult.RunResult.GeneratedTrees[0].GetTextAsync(TestContext.CancellationToken); myTypeSource.Should().ContainSourceCode(""" public static class MyNamespace_MyType { diff --git a/test/UnitTests/MSTest.SourceGeneration.UnitTests/Helpers/ConstantsTests.cs b/test/UnitTests/MSTest.SourceGeneration.UnitTests/Helpers/ConstantsTests.cs index a579ec298c..1944f5b664 100644 --- a/test/UnitTests/MSTest.SourceGeneration.UnitTests/Helpers/ConstantsTests.cs +++ b/test/UnitTests/MSTest.SourceGeneration.UnitTests/Helpers/ConstantsTests.cs @@ -1,7 +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 FluentAssertions; +using AwesomeAssertions; namespace Microsoft.Testing.Framework.SourceGeneration.UnitTests; diff --git a/test/UnitTests/MSTest.SourceGeneration.UnitTests/Helpers/GeneratorCompilationResultHelpers.cs b/test/UnitTests/MSTest.SourceGeneration.UnitTests/Helpers/GeneratorCompilationResultHelpers.cs index 6bd800f900..e9c4db7ff9 100644 --- a/test/UnitTests/MSTest.SourceGeneration.UnitTests/Helpers/GeneratorCompilationResultHelpers.cs +++ b/test/UnitTests/MSTest.SourceGeneration.UnitTests/Helpers/GeneratorCompilationResultHelpers.cs @@ -1,7 +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 FluentAssertions; +using AwesomeAssertions; using Microsoft.CodeAnalysis; using Microsoft.Testing.Framework.SourceGeneration.UnitTests.Helpers; diff --git a/test/UnitTests/MSTest.SourceGeneration.UnitTests/Helpers/MinimalTestRunner.cs b/test/UnitTests/MSTest.SourceGeneration.UnitTests/Helpers/MinimalTestRunner.cs index e00e391545..5537de1ad2 100644 --- a/test/UnitTests/MSTest.SourceGeneration.UnitTests/Helpers/MinimalTestRunner.cs +++ b/test/UnitTests/MSTest.SourceGeneration.UnitTests/Helpers/MinimalTestRunner.cs @@ -11,7 +11,7 @@ public static async Task RunAllAsync(string? testNameContainsFilter = null) #pragma warning disable IL2026 // Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code IEnumerable classes = Assembly.GetExecutingAssembly().GetTypes().Where(c => c.IsPublic); #pragma warning restore IL2026 // Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code - object[][] emptyRow = new[] { Array.Empty() }; + object[][] emptyRow = [[]]; int total = 0; int failed = 0; @@ -63,7 +63,7 @@ public static async Task RunAllAsync(string? testNameContainsFilter = null) object?[][]? rows = null; if (methodAttributes.Any(a => a.AttributeType == typeof(DataRowAttribute))) { - rows = methodAttributes + rows = [.. methodAttributes .Where(a => a.AttributeType == typeof(DataRowAttribute)) .SelectMany(a => a.ConstructorArguments.Select(arg => { @@ -79,8 +79,7 @@ public static async Task RunAllAsync(string? testNameContainsFilter = null) return [arg.Value]; } #pragma warning restore IDE0046 // Convert to conditional expression - })) - .ToArray(); + }))]; } foreach (object?[]? row in rows ?? emptyRow) diff --git a/test/UnitTests/MSTest.SourceGeneration.UnitTests/Helpers/SourceCodeAssertionExtensions.cs b/test/UnitTests/MSTest.SourceGeneration.UnitTests/Helpers/SourceCodeAssertionExtensions.cs index 0c9e28aae2..e2c6014a6b 100644 --- a/test/UnitTests/MSTest.SourceGeneration.UnitTests/Helpers/SourceCodeAssertionExtensions.cs +++ b/test/UnitTests/MSTest.SourceGeneration.UnitTests/Helpers/SourceCodeAssertionExtensions.cs @@ -1,7 +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 FluentAssertions.Execution; +using AwesomeAssertions.Execution; using Microsoft.CodeAnalysis.Text; diff --git a/test/UnitTests/MSTest.SourceGeneration.UnitTests/Helpers/SourceCodeAssertions.cs b/test/UnitTests/MSTest.SourceGeneration.UnitTests/Helpers/SourceCodeAssertions.cs index 369801c195..6d24195427 100644 --- a/test/UnitTests/MSTest.SourceGeneration.UnitTests/Helpers/SourceCodeAssertions.cs +++ b/test/UnitTests/MSTest.SourceGeneration.UnitTests/Helpers/SourceCodeAssertions.cs @@ -1,9 +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 FluentAssertions; -using FluentAssertions.Execution; -using FluentAssertions.Primitives; +using AwesomeAssertions; +using AwesomeAssertions.Execution; +using AwesomeAssertions.Primitives; namespace Microsoft.Testing.Framework.SourceGeneration.UnitTests.Helpers; diff --git a/test/UnitTests/MSTest.SourceGeneration.UnitTests/Helpers/SyntaxExtensions.cs b/test/UnitTests/MSTest.SourceGeneration.UnitTests/Helpers/SyntaxExtensions.cs index 5b7a153773..17133da1c3 100644 --- a/test/UnitTests/MSTest.SourceGeneration.UnitTests/Helpers/SyntaxExtensions.cs +++ b/test/UnitTests/MSTest.SourceGeneration.UnitTests/Helpers/SyntaxExtensions.cs @@ -32,7 +32,7 @@ public static string ShowWhitespace(this string text) { " ", "·" }, }; - var regex = new Regex(string.Join("|", map.Keys)); + var regex = new Regex(string.Join('|', map.Keys)); return regex.Replace(text, m => map[m.Value]); } diff --git a/test/UnitTests/MSTest.SourceGeneration.UnitTests/MSTest.SourceGeneration.UnitTests.csproj b/test/UnitTests/MSTest.SourceGeneration.UnitTests/MSTest.SourceGeneration.UnitTests.csproj index 6921c11bde..5813aaa051 100644 --- a/test/UnitTests/MSTest.SourceGeneration.UnitTests/MSTest.SourceGeneration.UnitTests.csproj +++ b/test/UnitTests/MSTest.SourceGeneration.UnitTests/MSTest.SourceGeneration.UnitTests.csproj @@ -10,7 +10,7 @@ - + diff --git a/test/UnitTests/MSTest.SourceGeneration.UnitTests/TestBase.cs b/test/UnitTests/MSTest.SourceGeneration.UnitTests/TestBase.cs index fcc6fa6db5..7a6d1c82ee 100644 --- a/test/UnitTests/MSTest.SourceGeneration.UnitTests/TestBase.cs +++ b/test/UnitTests/MSTest.SourceGeneration.UnitTests/TestBase.cs @@ -6,6 +6,4 @@ namespace Microsoft.Testing.Framework.SourceGeneration.UnitTests; /// /// Empty test base, because TestInfrastructure project depends on Testing.Framework, and we cannot use that. /// -public abstract class TestBase -{ -} +public abstract class TestBase; diff --git a/test/UnitTests/MSTest.SourceGeneration.UnitTests/TestUtilities/CSharpCodeFixVerifier.cs b/test/UnitTests/MSTest.SourceGeneration.UnitTests/TestUtilities/CSharpCodeFixVerifier.cs index 4d606aedc6..7b2cfb8660 100644 --- a/test/UnitTests/MSTest.SourceGeneration.UnitTests/TestUtilities/CSharpCodeFixVerifier.cs +++ b/test/UnitTests/MSTest.SourceGeneration.UnitTests/TestUtilities/CSharpCodeFixVerifier.cs @@ -23,7 +23,5 @@ public static async Task VerifyAnalyzerAsync(string source, params DiagnosticRes public static DiagnosticResult Diagnostic(DiagnosticDescriptor descriptor) => CSharpCodeFixVerifier.Diagnostic(descriptor); - public sealed class Test : CSharpCodeFixTest - { - } + public sealed class Test : CSharpCodeFixTest; } diff --git a/test/UnitTests/MSTest.SourceGeneration.UnitTests/TestUtilities/GeneratorTester.cs b/test/UnitTests/MSTest.SourceGeneration.UnitTests/TestUtilities/GeneratorTester.cs index 83f1290dcc..28dd385e13 100644 --- a/test/UnitTests/MSTest.SourceGeneration.UnitTests/TestUtilities/GeneratorTester.cs +++ b/test/UnitTests/MSTest.SourceGeneration.UnitTests/TestUtilities/GeneratorTester.cs @@ -3,7 +3,7 @@ using System.Collections.Immutable; -using FluentAssertions; +using AwesomeAssertions; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; @@ -30,8 +30,7 @@ public GeneratorTester(Func incrementalGeneratorFactory, public static GeneratorTester TestGraph { get; } = new( () => new TestNodesGenerator(), - new[] - { + [ // Microsoft.Testing.Platform dll Assembly.GetAssembly(typeof(IProperty))!.Location, @@ -45,13 +44,13 @@ public GeneratorTester(Func incrementalGeneratorFactory, Assembly.GetAssembly(typeof(TrxExceptionProperty))!.Location, // MSTest.TestFramework dll - Assembly.GetAssembly(typeof(TestClassAttribute))!.Location, - }); + Assembly.GetAssembly(typeof(TestClassAttribute))!.Location + ]); public static ImmutableArray? Net60MetadataReferences { get; set; } public async Task CompileAndExecuteAsync(string source, CancellationToken cancellationToken) - => await CompileAndExecuteAsync(new[] { source }, cancellationToken); + => await CompileAndExecuteAsync([source], cancellationToken); public async Task CompileAndExecuteAsync(string[] sources, CancellationToken cancellationToken) { @@ -79,10 +78,7 @@ public async Task CompileAndExecuteAsync(string[] so } } - MetadataReference[] metadataReferences = - Net60MetadataReferences.Value - .Concat(_additionalReferences.Select(loc => MetadataReference.CreateFromFile(loc))) - .ToArray(); + MetadataReference[] metadataReferences = [.. Net60MetadataReferences.Value, .. _additionalReferences.Select(loc => MetadataReference.CreateFromFile(loc))]; var compilation = CSharpCompilation.Create( "TestAssembly", @@ -92,7 +88,7 @@ public async Task CompileAndExecuteAsync(string[] so ISourceGenerator generator = _incrementalGeneratorFactory().AsSourceGenerator(); GeneratorDriver driver = CSharpGeneratorDriver.Create( - generators: new ISourceGenerator[] { generator }); + generators: [generator]); driver = driver.RunGeneratorsAndUpdateCompilation(compilation, out Compilation? outputCompilation, out ImmutableArray diagnostics, cancellationToken); diff --git a/test/UnitTests/MSTest.SourceGeneration.UnitTests/TestUtilities/TestingFrameworkVerifier.cs b/test/UnitTests/MSTest.SourceGeneration.UnitTests/TestUtilities/TestingFrameworkVerifier.cs index c870abcfe3..d21e00b691 100644 --- a/test/UnitTests/MSTest.SourceGeneration.UnitTests/TestUtilities/TestingFrameworkVerifier.cs +++ b/test/UnitTests/MSTest.SourceGeneration.UnitTests/TestUtilities/TestingFrameworkVerifier.cs @@ -11,7 +11,7 @@ namespace Microsoft.Testing.Framework.SourceGeneration.UnitTests.TestUtilities; internal sealed class TestingFrameworkVerifier : IVerifier { public TestingFrameworkVerifier() - : this(ImmutableStack.Empty) + : this([]) { } @@ -20,7 +20,7 @@ internal TestingFrameworkVerifier(ImmutableStack context) public ImmutableStack Context { get; } - public void Empty(string collectionName, IEnumerable collection) => Assert.IsFalse(collection?.Any() == true, CreateMessage($"expected '{collectionName}' to be empty, contains '{collection?.Count()}' elements")); + public void Empty(string collectionName, IEnumerable collection) => Assert.AreNotEqual(true, collection?.Any(), CreateMessage($"expected '{collectionName}' to be empty, contains '{collection?.Count()}' elements")); public void Equal(T expected, T actual, string? message = null) { @@ -63,7 +63,7 @@ public void False([DoesNotReturnIf(true)] bool assert, string? message = null) public void LanguageIsSupported(string language) => Assert.IsFalse(language is not LanguageNames.CSharp and not LanguageNames.VisualBasic, CreateMessage($"Unsupported Language: '{language}'")); - public void NotEmpty(string collectionName, IEnumerable collection) => Assert.IsTrue(collection?.Any() == true, CreateMessage($"expected '{collectionName}' to be non-empty, contains")); + public void NotEmpty(string collectionName, IEnumerable collection) => Assert.IsNotEmpty(collection, CreateMessage($"expected '{collectionName}' to be non-empty, contains")); public IVerifier PushContext(string context) { @@ -108,9 +108,7 @@ private sealed class SequenceEqualEnumerableEqualityComparer : IEqualityCompa private readonly IEqualityComparer _itemEqualityComparer; public SequenceEqualEnumerableEqualityComparer(IEqualityComparer? itemEqualityComparer) - { - _itemEqualityComparer = itemEqualityComparer ?? EqualityComparer.Default; - } + => _itemEqualityComparer = itemEqualityComparer ?? EqualityComparer.Default; public bool Equals(IEnumerable? x, IEnumerable? y) => ReferenceEquals(x, y) diff --git a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/AssemblyResolverTests.cs b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/AssemblyResolverTests.cs index 4e5035c30c..3af7639fa0 100644 --- a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/AssemblyResolverTests.cs +++ b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/AssemblyResolverTests.cs @@ -24,7 +24,7 @@ public void AddSubDirectoriesShouldReturnSubDirectoriesInDfsOrder() @"C:\unitTesting\b", ]; - TestableAssemblyResolver assemblyResolver = new(new List { @"c:\dummy" }) + TestableAssemblyResolver assemblyResolver = new([@"c:\dummy"]) { DoesDirectoryExistSetter = (str) => true, GetDirectoriesSetter = (str) => @@ -42,7 +42,7 @@ public void AddSubDirectoriesShouldReturnSubDirectoriesInDfsOrder() return [@"C:\unitTesting\a\c\d"]; } - return new List().ToArray(); + return []; }, }; @@ -88,7 +88,7 @@ public void OnResolveShouldAddSearchDirectoryListOnANeedToBasis() return [@"C:\FunctionalTesting\c"]; } - return new List().ToArray(); + return []; }; assemblyResolver.SearchAssemblySetter = @@ -147,7 +147,7 @@ public void ReflectionOnlyOnResolveShouldNotReturnACachedDefaultLoadedAssembly() { Assembly currentAssembly = typeof(AssemblyResolverTests).Assembly; string currentAssemblyPath = Path.GetDirectoryName(currentAssembly.Location); - var assemblyResolver = new TestableAssemblyResolver(new List { currentAssemblyPath }); + var assemblyResolver = new TestableAssemblyResolver([currentAssemblyPath]); bool isAssemblyLoaded = false; bool isAssemblyReflectionOnlyLoaded = false; diff --git a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Deployment/DeploymentItemTests.cs b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Deployment/DeploymentItemTests.cs index 34b48cb028..4eb8834a36 100644 --- a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Deployment/DeploymentItemTests.cs +++ b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Deployment/DeploymentItemTests.cs @@ -1,8 +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.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices; using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Deployment; +using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Resources; using TestFramework.ForTestingMSTest; diff --git a/test/UnitTests/MSTestAdapter.UnitTests/Discovery/AssemblyEnumeratorTests.cs b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Discovery/AssemblyEnumeratorTests.cs similarity index 95% rename from test/UnitTests/MSTestAdapter.UnitTests/Discovery/AssemblyEnumeratorTests.cs rename to test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Discovery/AssemblyEnumeratorTests.cs index 4a34f4c0bb..6e72cae9df 100644 --- a/test/UnitTests/MSTestAdapter.UnitTests/Discovery/AssemblyEnumeratorTests.cs +++ b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Discovery/AssemblyEnumeratorTests.cs @@ -7,6 +7,7 @@ using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Discovery; using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Helpers; using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel; +using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Resources; using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.UnitTests.TestableImplementations; using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging; @@ -26,7 +27,7 @@ public class AssemblyEnumeratorTests : TestContainer public AssemblyEnumeratorTests() { _assemblyEnumerator = new AssemblyEnumerator(); - _warnings = new List(); + _warnings = []; _testablePlatformServiceProvider = new TestablePlatformServiceProvider(); PlatformServiceProvider.Instance = _testablePlatformServiceProvider; @@ -94,7 +95,7 @@ public void GetTypesShouldReturnSetOfDefinedTypes() // Setup mocks mockAssembly.Setup(a => a.GetTypes()).Returns(expectedTypes); - IReadOnlyList types = AssemblyEnumerator.GetTypes(mockAssembly.Object, string.Empty, _warnings); + Type[] types = AssemblyEnumerator.GetTypes(mockAssembly.Object, string.Empty, _warnings); Verify(expectedTypes.SequenceEqual(types)); } @@ -116,10 +117,10 @@ public void GetTypesShouldReturnReflectionTypeLoadExceptionTypesOnException() // Setup mocks mockAssembly.Setup(a => a.GetTypes()).Throws(new ReflectionTypeLoadException(reflectedTypes, null)); - IReadOnlyList types = AssemblyEnumerator.GetTypes(mockAssembly.Object, string.Empty, _warnings); + Type[] types = AssemblyEnumerator.GetTypes(mockAssembly.Object, string.Empty, _warnings); Verify(types is not null); - Verify(reflectedTypes.Equals(types)); + Verify(reflectedTypes.SequenceEqual(types)); } public void GetTypesShouldLogWarningsWhenReflectionFailsWithLoaderExceptions() @@ -131,7 +132,10 @@ public void GetTypesShouldLogWarningsWhenReflectionFailsWithLoaderExceptions() mockAssembly.Setup(a => a.GetTypes()).Throws(new ReflectionTypeLoadException(null, exceptions)); mockAssembly.Setup(a => a.GetTypes()).Throws(new ReflectionTypeLoadException(null, exceptions)); - IReadOnlyList types = AssemblyEnumerator.GetTypes(mockAssembly.Object, "DummyAssembly", _warnings); + Type[] types = AssemblyEnumerator.GetTypes(mockAssembly.Object, "DummyAssembly", _warnings); + + // Depending on the TFM, .NET either gives us null or empty array. + Verify(types is null || types.Length == 0); Verify(_warnings.Count == 1); Verify(_warnings.ToList().Contains( @@ -256,7 +260,7 @@ public void EnumerateAssemblyShouldReturnTestElementsForAType() _testablePlatformServiceProvider.MockFileOperations.Setup(fo => fo.LoadAssembly("DummyAssembly", false)) .Returns(mockAssembly.Object); testableAssemblyEnumerator.MockTypeEnumerator.Setup(te => te.Enumerate(_warnings)) - .Returns(new List { unitTestElement }); + .Returns([unitTestElement]); AssemblyEnumerationResult result = testableAssemblyEnumerator.EnumerateAssembly("DummyAssembly"); _warnings.AddRange(result.Warnings); @@ -389,6 +393,9 @@ private static Mock CreateMockTestableAssembly() // The mock must be configured with a return value for GetCustomAttributes for this attribute type, but the // actual return value is irrelevant for these tests. + // NOTE: Don't convert Array.Empty() to [] as it will cause an InvalidCastException. + // [] will produce `object[]`, then it will fail to cast here: + // https://github.com/dotnet/runtime/blob/4252c8d09b2ec537928f34dad269f02f167c8ce5/src/coreclr/System.Private.CoreLib/src/System/Attribute.CoreCLR.cs#L710 mockAssembly .Setup(a => a.GetCustomAttributes( typeof(DiscoverInternalsAttribute), diff --git a/test/UnitTests/MSTestAdapter.UnitTests/Discovery/AssemblyEnumeratorWrapperTests.cs b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Discovery/AssemblyEnumeratorWrapperTests.cs similarity index 98% rename from test/UnitTests/MSTestAdapter.UnitTests/Discovery/AssemblyEnumeratorWrapperTests.cs rename to test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Discovery/AssemblyEnumeratorWrapperTests.cs index e652995ef1..32a73cdba7 100644 --- a/test/UnitTests/MSTestAdapter.UnitTests/Discovery/AssemblyEnumeratorWrapperTests.cs +++ b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Discovery/AssemblyEnumeratorWrapperTests.cs @@ -3,14 +3,13 @@ using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter; using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Discovery; +using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Resources; using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.UnitTests.TestableImplementations; using Moq; using TestFramework.ForTestingMSTest; -using UTF = Microsoft.VisualStudio.TestTools.UnitTesting; - namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.UnitTests.Discovery; public class AssemblyEnumeratorWrapperTests : TestContainer @@ -23,7 +22,7 @@ public class AssemblyEnumeratorWrapperTests : TestContainer public AssemblyEnumeratorWrapperTests() { _testableAssemblyEnumeratorWrapper = new AssemblyEnumeratorWrapper(); - _warnings = new List(); + _warnings = []; _testablePlatformServiceProvider = new TestablePlatformServiceProvider(); PlatformServiceProvider.Instance = _testablePlatformServiceProvider; @@ -178,11 +177,11 @@ private void SetupMocks(string assemblyName, bool doesFileExist, bool isAssembly #region dummy implementations. - [UTF.TestClass] + [TestClass] public class ValidTestClass { // This is just a dummy method for test validation. - [UTF.TestMethod] + [TestMethod] public void ValidTestMethod() { } diff --git a/test/UnitTests/MSTestAdapter.UnitTests/Discovery/TestMethodValidatorTests.cs b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Discovery/TestMethodValidatorTests.cs similarity index 94% rename from test/UnitTests/MSTestAdapter.UnitTests/Discovery/TestMethodValidatorTests.cs rename to test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Discovery/TestMethodValidatorTests.cs index 10067ed9da..faf2d778f2 100644 --- a/test/UnitTests/MSTestAdapter.UnitTests/Discovery/TestMethodValidatorTests.cs +++ b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Discovery/TestMethodValidatorTests.cs @@ -8,8 +8,6 @@ using TestFramework.ForTestingMSTest; -using UTF = Microsoft.VisualStudio.TestTools.UnitTesting; - namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.UnitTests.Discovery; public class TestMethodValidatorTests : TestContainer @@ -34,7 +32,7 @@ public TestMethodValidatorTests() public void IsValidTestMethodShouldReturnFalseForMethodsWithoutATestMethodAttributeOrItsDerivedAttributes() { _mockReflectHelper.Setup( - rh => rh.IsDerivedAttributeDefined(It.IsAny(), false)).Returns(false); + rh => rh.IsAttributeDefined(It.IsAny(), false)).Returns(false); Verify(!_testMethodValidator.IsValidTestMethod(_mockMethodInfo.Object, _type, _warnings)); } @@ -57,7 +55,7 @@ public void IsValidTestMethodShouldNotReportWarningsForGenericTestMethodDefiniti SetupTestMethod(); _mockReflectHelper - .Setup(x => x.GetFirstNonDerivedAttributeOrDefault(_mockMethodInfo.Object, false)) + .Setup(x => x.GetFirstAttributeOrDefault(_mockMethodInfo.Object, false)) .Returns(default(AsyncStateMachineAttribute?)); _mockMethodInfo.Setup(mi => mi.IsGenericMethodDefinition).Returns(true); @@ -114,7 +112,7 @@ public void IsValidTestMethodShouldReturnFalseForAsyncMethodsWithNonTaskReturnTy MethodInfo methodInfo = typeof(DummyTestClass).GetMethod( "AsyncMethodWithVoidReturnType", BindingFlags.Instance | BindingFlags.Public)!; - _mockReflectHelper.Setup(_mockReflectHelper => _mockReflectHelper.GetFirstNonDerivedAttributeOrDefault(methodInfo, false)) + _mockReflectHelper.Setup(_mockReflectHelper => _mockReflectHelper.GetFirstAttributeOrDefault(methodInfo, false)) .CallBase(); Verify(!_testMethodValidator.IsValidTestMethod(methodInfo, typeof(DummyTestClass), _warnings)); @@ -190,7 +188,7 @@ public void WhenDiscoveryOfInternalsIsEnabledIsValidTestMethodShouldReturnFalseF private void SetupTestMethod() => _mockReflectHelper.Setup( - rh => rh.IsDerivedAttributeDefined(It.IsAny(), false)).Returns(true); + rh => rh.IsAttributeDefined(It.IsAny(), false)).Returns(true); } #region Dummy types diff --git a/test/UnitTests/MSTestAdapter.UnitTests/Discovery/TypeEnumeratorTests.MockedMethodInfoWithExtraAttributes.cs b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Discovery/TypeEnumeratorTests.MockedMethodInfoWithExtraAttributes.cs similarity index 100% rename from test/UnitTests/MSTestAdapter.UnitTests/Discovery/TypeEnumeratorTests.MockedMethodInfoWithExtraAttributes.cs rename to test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Discovery/TypeEnumeratorTests.MockedMethodInfoWithExtraAttributes.cs diff --git a/test/UnitTests/MSTestAdapter.UnitTests/Discovery/TypeEnumeratorTests.cs b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Discovery/TypeEnumeratorTests.cs similarity index 99% rename from test/UnitTests/MSTestAdapter.UnitTests/Discovery/TypeEnumeratorTests.cs rename to test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Discovery/TypeEnumeratorTests.cs index 43d39526a2..8def534a6f 100644 --- a/test/UnitTests/MSTestAdapter.UnitTests/Discovery/TypeEnumeratorTests.cs +++ b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Discovery/TypeEnumeratorTests.cs @@ -33,7 +33,7 @@ public TypeEnumeratorTests() _mockTypeValidator = new Mock(MockBehavior.Default, _mockReflectHelper.Object); _mockTestMethodValidator = new Mock(MockBehavior.Default, _mockReflectHelper.Object, false); - _warnings = new List(); + _warnings = []; _mockMessageLogger = new Mock(); _testablePlatformServiceProvider = new TestablePlatformServiceProvider(); @@ -403,7 +403,8 @@ public void GetTestFromMethodShouldSetDescription() MSTest.TestAdapter.ObjectModel.UnitTestElement testElement = typeEnumerator.GetTestFromMethod(methodInfo, true, _warnings); - Verify(testElement.Description == "Dummy description"); + Verify(testElement.Traits is not null); + Verify(testElement.Traits.Any(t => t.Name == "Description" && t.Value == "Dummy description")); } public void GetTestFromMethodShouldSetWorkItemIds() diff --git a/test/UnitTests/MSTestAdapter.UnitTests/Discovery/TypeValidatorTests.cs b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Discovery/TypeValidatorTests.cs similarity index 87% rename from test/UnitTests/MSTestAdapter.UnitTests/Discovery/TypeValidatorTests.cs rename to test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Discovery/TypeValidatorTests.cs index 1fedee983c..7745cdc0cc 100644 --- a/test/UnitTests/MSTestAdapter.UnitTests/Discovery/TypeValidatorTests.cs +++ b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Discovery/TypeValidatorTests.cs @@ -1,17 +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. -using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter; using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Discovery; using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Helpers; +using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Resources; using Moq; using TestFramework.ForTestingMSTest; -using UTF = Microsoft.VisualStudio.TestTools.UnitTesting; -using UTFExtension = Microsoft.VisualStudio.TestTools.UnitTesting; - namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.UnitTests.Discovery; public class TypeValidatorTests : TestContainer @@ -41,15 +38,15 @@ public TypeValidatorTests() public void IsValidTestClassShouldReturnFalseForClassesNotHavingTestClassAttributeOrDerivedAttributeTypes() { - _mockReflectHelper.Setup(rh => rh.IsNonDerivedAttributeDefined(It.IsAny(), false)).Returns(false); + _mockReflectHelper.Setup(rh => rh.IsAttributeDefined(It.IsAny(), false)).Returns(false); Verify(!_typeValidator.IsValidTestClass(typeof(TypeValidatorTests), _warnings)); } public void IsValidTestClassShouldReturnTrueForClassesMarkedByAnAttributeDerivedFromTestClass() { - _mockReflectHelper.Setup(rh => rh.IsNonDerivedAttributeDefined(It.IsAny(), false)).Returns(false); + _mockReflectHelper.Setup(rh => rh.IsAttributeDefined(It.IsAny(), false)).Returns(false); _mockReflectHelper.Setup( - rh => rh.IsDerivedAttributeDefined(It.IsAny(), false)).Returns(true); + rh => rh.IsAttributeDefined(It.IsAny(), false)).Returns(true); Verify(_typeValidator.IsValidTestClass(typeof(TypeValidatorTests), _warnings)); } @@ -240,8 +237,8 @@ public void AllTypesContainAllPrivateClasses() { // The names of private types are not accessible by nameof or typeof, ensure that we have them in the // list of our test types, to avoid bugs caused by typos. - string[] allTypes = GetAllTestTypes().Select(t => t.Name).ToArray(); - string[] privateTypes = typeof(PrivateClassNames).GetProperties().Select(n => n.Name).ToArray(); + string[] allTypes = [.. GetAllTestTypes().Select(t => t.Name)]; + string[] privateTypes = [.. typeof(PrivateClassNames).GetProperties().Select(n => n.Name)]; Verify(privateTypes.Length >= 1); foreach (string type in privateTypes) @@ -264,9 +261,9 @@ public void TypeHasValidAccessibilityShouldReturnTrueForAllPublicTypesIncludingN ]; bool discoverInternal = false; - string[] actualDiscoveredTypes = allTypes + string[] actualDiscoveredTypes = [.. allTypes .Where(t => TypeValidator.TypeHasValidAccessibility(t, discoverInternal)) - .Select(t => t.Name).ToArray(); + .Select(t => t.Name)]; Array.Sort(actualDiscoveredTypes); Array.Sort(expectedDiscoveredTypes); @@ -307,9 +304,9 @@ public void TypeHasValidAccessibilityShouldReturnFalseForAllTypesThatAreNotPubli ]; bool discoverInternal = false; - string[] actualDiscoveredTypes = allTypes + string[] actualDiscoveredTypes = [.. allTypes .Where(t => !TypeValidator.TypeHasValidAccessibility(t, discoverInternal)) - .Select(t => t.Name).ToArray(); + .Select(t => t.Name)]; Array.Sort(actualDiscoveredTypes); Array.Sort(expectedNonDiscoveredTypes); @@ -342,9 +339,9 @@ public void TypeHasValidAccessibilityShouldReturnTrueForAllPublicAndInternalType ]; bool discoverInternal = true; - string[] actualDiscoveredTypes = allTypes + string[] actualDiscoveredTypes = [.. allTypes .Where(t => TypeValidator.TypeHasValidAccessibility(t, discoverInternal)) - .Select(t => t.Name).ToArray(); + .Select(t => t.Name)]; Array.Sort(actualDiscoveredTypes); Array.Sort(expectedDiscoveredTypes); @@ -372,9 +369,9 @@ public void TypeHasValidAccessibilityShouldReturnFalseForAllTypesThatAreNotPubli ]; bool discoverInternal = true; - string[] actualDiscoveredTypes = allTypes + string[] actualDiscoveredTypes = [.. allTypes .Where(t => !TypeValidator.TypeHasValidAccessibility(t, discoverInternal)) - .Select(t => t.Name).ToArray(); + .Select(t => t.Name)]; Array.Sort(actualDiscoveredTypes); Array.Sort(expectedNonDiscoveredTypes); @@ -384,16 +381,16 @@ public void TypeHasValidAccessibilityShouldReturnFalseForAllTypesThatAreNotPubli private static Type[] GetAllTestTypes() { 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(); + Type[] nestedTypes = [.. types.SelectMany(t => t.GetNestedTypes(BindingFlags.Public | BindingFlags.NonPublic))]; + Type[] nestedNestedTypes = [.. nestedTypes.SelectMany(t => t.GetNestedTypes(BindingFlags.Public | BindingFlags.NonPublic))]; + Type[] allTypes = [.. new[] { types, nestedTypes, nestedNestedTypes }.SelectMany(t => t)]; return allTypes; } #endregion #region private methods - private void SetupTestClass() => _mockReflectHelper.Setup(rh => rh.IsDerivedAttributeDefined(It.IsAny(), false)).Returns(true); + private void SetupTestClass() => _mockReflectHelper.Setup(rh => rh.IsAttributeDefined(It.IsAny(), false)).Returns(true); #endregion } @@ -412,33 +409,33 @@ public class GenericClass; public class ClassWithTestContextGetterOnly { - public UTFExtension.TestContext TestContext { get; } = null!; + public TestContext TestContext => null!; } public class ClassWithTestContextPrivateSetter { - public UTFExtension.TestContext TestContext { get; private set; } = null!; + public TestContext TestContext { get; private set; } = null!; } public class ClassWithStaticTestContext { - public static UTFExtension.TestContext TestContext { get; set; } = null!; + public static TestContext TestContext { get; set; } = null!; } public abstract class ClassWithAbstractTestContext { - public abstract UTFExtension.TestContext TestContext { get; set; } + public abstract TestContext TestContext { get; set; } } public class ClassWithTestContext { - public UTFExtension.TestContext TestContext { get; set; } = null!; + public TestContext TestContext { get; set; } = null!; } public class GenericClassWithProperty { #pragma warning disable CA1000 // Do not declare static members on generic types - public static T ReturnAStableSomething { get; } = default!; + public static T ReturnAStableSomething => default!; #pragma warning restore CA1000 // Do not declare static members on generic types public T ReturnSomething { get; set; } = default!; @@ -449,14 +446,14 @@ public class GenericClassWithProperty public class GenericClassWithTestContext { #pragma warning disable CA1000 // Do not declare static members on generic types - public static T ReturnAStableSomething { get; } = default!; + public static T ReturnAStableSomething => default!; #pragma warning restore CA1000 // Do not declare static members on generic types public T ReturnSomething { get; set; } = default!; public bool Something { get; } - public UTFExtension.TestContext TestContext { get; set; } = null!; + public TestContext TestContext { get; set; } = null!; } public class PublicTestClass; @@ -550,25 +547,25 @@ public sealed class PublicClassNestedInPrivateClassNestedInInternalClass; /// internal class PrivateClassNames { - public string ProtectedInteralNestedClassInPublicClass { get; } = null!; + public string ProtectedInteralNestedClassInPublicClass => null!; - public string ProtectedNestedClassInPublicClass { get; } = null!; + public string ProtectedNestedClassInPublicClass => null!; - public string PrivateProtectedNestedClassInPublicClass { get; } = null!; + public string PrivateProtectedNestedClassInPublicClass => null!; - public string PrivateClassNestedInPublicClass { get; } = null!; + public string PrivateClassNestedInPublicClass => null!; - public string ProtectedInteralClassNestedInInternalClass { get; } = null!; + public string ProtectedInteralClassNestedInInternalClass => null!; - public string ProtectedClassNestedInInternalClass { get; } = null!; + public string ProtectedClassNestedInInternalClass => null!; - public string PrivateProtectedClassNestedInInternalClass { get; } = null!; + public string PrivateProtectedClassNestedInInternalClass => null!; - public string PrivateClassNestedInInternalClass { get; } = null!; + public string PrivateClassNestedInInternalClass => null!; - public string PublicClassNestedInPrivateClassNestedInPublicClass { get; } = null!; + public string PublicClassNestedInPrivateClassNestedInPublicClass => null!; - public string PublicClassNestedInPrivateClassNestedInInternalClass { get; } = null!; + public string PublicClassNestedInPrivateClassNestedInInternalClass => null!; } #endregion diff --git a/test/UnitTests/MSTestAdapter.UnitTests/Discovery/UnitTestDiscovererTests.cs b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Discovery/UnitTestDiscovererTests.cs similarity index 99% rename from test/UnitTests/MSTestAdapter.UnitTests/Discovery/UnitTestDiscovererTests.cs rename to test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Discovery/UnitTestDiscovererTests.cs index 9911a3ddf6..cd441bf364 100644 --- a/test/UnitTests/MSTestAdapter.UnitTests/Discovery/UnitTestDiscovererTests.cs +++ b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Discovery/UnitTestDiscovererTests.cs @@ -434,7 +434,7 @@ internal sealed class TestableTestCaseFilterExpression : ITestCaseFilterExpressi public TestableTestCaseFilterExpression(Func matchTestCase) => _matchTest = matchTestCase; - public string TestCaseFilterValue { get; } = null!; + public string TestCaseFilterValue => null!; public bool MatchTestCase(TestCase testCase, Func propertyValueProvider) => _matchTest(testCase); } diff --git a/test/UnitTests/MSTestAdapter.UnitTests/Execution/ClassCleanupManagerTests.cs b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Execution/ClassCleanupManagerTests.cs similarity index 86% rename from test/UnitTests/MSTestAdapter.UnitTests/Execution/ClassCleanupManagerTests.cs rename to test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Execution/ClassCleanupManagerTests.cs index 50d3f59707..76c86755e8 100644 --- a/test/UnitTests/MSTestAdapter.UnitTests/Execution/ClassCleanupManagerTests.cs +++ b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Execution/ClassCleanupManagerTests.cs @@ -24,13 +24,13 @@ public void AssemblyCleanupRunsAfterAllTestsFinishEvenIfWeScheduleTheSameTestMul // Setting 2 of the same test to run, we should run assembly cleanup after both these tests // finish, not after the first one finishes. - List testsToRun = new() - { - new(testMethod), + List testsToRun = + [ new(testMethod), - }; + new(testMethod) + ]; - var classCleanupManager = new ClassCleanupManager(testsToRun, ClassCleanupBehavior.EndOfClass, ClassCleanupBehavior.EndOfClass, reflectHelper); + var classCleanupManager = new ClassCleanupManager(testsToRun, ClassCleanupBehavior.EndOfClass, reflectHelper); TestClassInfo testClassInfo = new(typeof(FakeTestClass), null!, true, new TestClassAttribute(), null!) { @@ -38,13 +38,13 @@ public void AssemblyCleanupRunsAfterAllTestsFinishEvenIfWeScheduleTheSameTestMul ClassCleanupMethod = classCleanupMethodInfo, }; TestMethodInfo testMethodInfo = new(methodInfo, testClassInfo, null!); - classCleanupManager.MarkTestComplete(testMethodInfo, testMethod, out bool shouldRunEndOfClassCleanup); + classCleanupManager.MarkTestComplete(testMethodInfo, out bool shouldRunEndOfClassCleanup); // The cleanup should not run here yet, we have 1 remaining test to run. Assert.IsFalse(shouldRunEndOfClassCleanup); Assert.IsFalse(classCleanupManager.ShouldRunEndOfAssemblyCleanup); - classCleanupManager.MarkTestComplete(testMethodInfo, testMethod, out shouldRunEndOfClassCleanup); + classCleanupManager.MarkTestComplete(testMethodInfo, out shouldRunEndOfClassCleanup); // The cleanup should run here. Assert.IsTrue(shouldRunEndOfClassCleanup); Assert.IsTrue(classCleanupManager.ShouldRunEndOfAssemblyCleanup); diff --git a/test/UnitTests/MSTestAdapter.UnitTests/Execution/LogMessageListenerTests.cs b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Execution/LogMessageListenerTests.cs similarity index 100% rename from test/UnitTests/MSTestAdapter.UnitTests/Execution/LogMessageListenerTests.cs rename to test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Execution/LogMessageListenerTests.cs diff --git a/test/UnitTests/MSTestAdapter.UnitTests/Execution/TcmTestPropertiesProviderTests.cs b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Execution/TcmTestPropertiesProviderTests.cs similarity index 75% rename from test/UnitTests/MSTestAdapter.UnitTests/Execution/TcmTestPropertiesProviderTests.cs rename to test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Execution/TcmTestPropertiesProviderTests.cs index 9c14439ad2..320b2de9a5 100644 --- a/test/UnitTests/MSTestAdapter.UnitTests/Execution/TcmTestPropertiesProviderTests.cs +++ b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Execution/TcmTestPropertiesProviderTests.cs @@ -2,39 +2,38 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution; +using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices; using Microsoft.VisualStudio.TestPlatform.ObjectModel; using TestFramework.ForTestingMSTest; -using TestAdapterConstants = Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Constants; - namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.UnitTests.Execution; public class TcmTestPropertiesProviderTests : TestContainer { private readonly TestProperty[] _tcmKnownProperties = [ - TestAdapterConstants.TestRunIdProperty, - TestAdapterConstants.TestPlanIdProperty, - TestAdapterConstants.BuildConfigurationIdProperty, - TestAdapterConstants.BuildDirectoryProperty, - TestAdapterConstants.BuildFlavorProperty, - TestAdapterConstants.BuildNumberProperty, - TestAdapterConstants.BuildPlatformProperty, - TestAdapterConstants.BuildUriProperty, - TestAdapterConstants.TfsServerCollectionUrlProperty, - TestAdapterConstants.TfsTeamProjectProperty, - TestAdapterConstants.IsInLabEnvironmentProperty, - TestAdapterConstants.TestCaseIdProperty, - TestAdapterConstants.TestConfigurationIdProperty, - TestAdapterConstants.TestConfigurationNameProperty, - TestAdapterConstants.TestPointIdProperty, + EngineConstants.TestRunIdProperty, + EngineConstants.TestPlanIdProperty, + EngineConstants.BuildConfigurationIdProperty, + EngineConstants.BuildDirectoryProperty, + EngineConstants.BuildFlavorProperty, + EngineConstants.BuildNumberProperty, + EngineConstants.BuildPlatformProperty, + EngineConstants.BuildUriProperty, + EngineConstants.TfsServerCollectionUrlProperty, + EngineConstants.TfsTeamProjectProperty, + EngineConstants.IsInLabEnvironmentProperty, + EngineConstants.TestCaseIdProperty, + EngineConstants.TestConfigurationIdProperty, + EngineConstants.TestConfigurationNameProperty, + EngineConstants.TestPointIdProperty, ]; public void GetTcmPropertiesShouldReturnEmptyDictionaryIfTestCaseIsNull() { - IDictionary tcmProperties = TcmTestPropertiesProvider.GetTcmProperties(null); - Verify(tcmProperties.Count == 0); + IDictionary? tcmProperties = TcmTestPropertiesProvider.GetTcmProperties(null); + Verify(tcmProperties is null); } public void GetTcmPropertiesShouldReturnEmptyDictionaryIfTestCaseIdIsZero() @@ -60,8 +59,8 @@ public void GetTcmPropertiesShouldReturnEmptyDictionaryIfTestCaseIdIsZero() ]; SetTestCaseProperties(testCase, propertiesValue); - IDictionary tcmProperties = TcmTestPropertiesProvider.GetTcmProperties(testCase); - Verify(tcmProperties.Count == 0); + IDictionary? tcmProperties = TcmTestPropertiesProvider.GetTcmProperties(testCase); + Verify(tcmProperties is null); } public void GetTcmPropertiesShouldGetAllPropertiesFromTestCase() @@ -87,7 +86,7 @@ public void GetTcmPropertiesShouldGetAllPropertiesFromTestCase() ]; SetTestCaseProperties(testCase, propertiesValue); - IDictionary tcmProperties = TcmTestPropertiesProvider.GetTcmProperties(testCase); + IDictionary? tcmProperties = TcmTestPropertiesProvider.GetTcmProperties(testCase); VerifyTcmProperties(tcmProperties, testCase); } @@ -115,7 +114,7 @@ public void GetTcmPropertiesShouldCopyMultiplePropertiesCorrectlyFromTestCase() 345 ]; SetTestCaseProperties(testCase1, propertiesValue1); - IDictionary tcmProperties1 = TcmTestPropertiesProvider.GetTcmProperties(testCase1); + IDictionary? tcmProperties1 = TcmTestPropertiesProvider.GetTcmProperties(testCase1); VerifyTcmProperties(tcmProperties1, testCase1); // Verify 2nd call. @@ -139,7 +138,7 @@ public void GetTcmPropertiesShouldCopyMultiplePropertiesCorrectlyFromTestCase() 346 ]; SetTestCaseProperties(testCase2, propertiesValue2); - IDictionary tcmProperties2 = TcmTestPropertiesProvider.GetTcmProperties(testCase2); + IDictionary? tcmProperties2 = TcmTestPropertiesProvider.GetTcmProperties(testCase2); VerifyTcmProperties(tcmProperties2, testCase2); } @@ -166,7 +165,7 @@ public void GetTcmPropertiesShouldHandleDuplicateTestsProperlyFromTestCase() 345 ]; SetTestCaseProperties(testCase1, propertiesValue1); - IDictionary tcmProperties1 = TcmTestPropertiesProvider.GetTcmProperties(testCase1); + IDictionary? tcmProperties1 = TcmTestPropertiesProvider.GetTcmProperties(testCase1); VerifyTcmProperties(tcmProperties1, testCase1); // Verify 2nd call. @@ -190,7 +189,7 @@ public void GetTcmPropertiesShouldHandleDuplicateTestsProperlyFromTestCase() 346 ]; SetTestCaseProperties(testCase2, propertiesValue2); - IDictionary tcmProperties2 = TcmTestPropertiesProvider.GetTcmProperties(testCase2); + IDictionary? tcmProperties2 = TcmTestPropertiesProvider.GetTcmProperties(testCase2); VerifyTcmProperties(tcmProperties2, testCase2); // Verify 3rd call. @@ -214,15 +213,15 @@ public void GetTcmPropertiesShouldHandleDuplicateTestsProperlyFromTestCase() 347 ]; SetTestCaseProperties(testCase3, propertiesValue3); - IDictionary tcmProperties3 = TcmTestPropertiesProvider.GetTcmProperties(testCase3); + IDictionary? tcmProperties3 = TcmTestPropertiesProvider.GetTcmProperties(testCase3); VerifyTcmProperties(tcmProperties3, testCase3); } private void SetTestCaseProperties(TestCase testCase, object[] propertiesValue) { - System.Collections.IEnumerator tcmKnownPropertiesEnumerator = _tcmKnownProperties.GetEnumerator(); + IEnumerator tcmKnownPropertiesEnumerator = _tcmKnownProperties.GetEnumerator(); - System.Collections.IEnumerator propertiesValueEnumerator = propertiesValue.GetEnumerator(); + IEnumerator propertiesValueEnumerator = propertiesValue.GetEnumerator(); while (tcmKnownPropertiesEnumerator.MoveNext() && propertiesValueEnumerator.MoveNext()) { object? property = tcmKnownPropertiesEnumerator.Current; @@ -231,8 +230,9 @@ private void SetTestCaseProperties(TestCase testCase, object[] propertiesValue) } } - private void VerifyTcmProperties(IDictionary tcmProperties, TestCase testCase) + private void VerifyTcmProperties(IDictionary? tcmProperties, TestCase testCase) { + Verify(tcmProperties is not null); foreach (TestProperty property in _tcmKnownProperties) { Verify(testCase.GetPropertyValue(property)!.Equals(tcmProperties[property])); diff --git a/test/UnitTests/MSTestAdapter.UnitTests/Execution/TestAssemblyInfoTests.cs b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Execution/TestAssemblyInfoTests.cs similarity index 96% rename from test/UnitTests/MSTestAdapter.UnitTests/Execution/TestAssemblyInfoTests.cs rename to test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Execution/TestAssemblyInfoTests.cs index c2f674af8d..e95b9c01cc 100644 --- a/test/UnitTests/MSTestAdapter.UnitTests/Execution/TestAssemblyInfoTests.cs +++ b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Execution/TestAssemblyInfoTests.cs @@ -9,7 +9,6 @@ using TestFramework.ForTestingMSTest; using UTF = Microsoft.VisualStudio.TestTools.UnitTesting; -using UTFExtension = Microsoft.VisualStudio.TestTools.UnitTesting; namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.UnitTests.Execution; @@ -19,14 +18,14 @@ public class TestAssemblyInfoTests : TestContainer private readonly MethodInfo _dummyMethodInfo; - private readonly UTFExtension.TestContext _testContext; + private readonly TestContext _testContext; public TestAssemblyInfoTests() { _testAssemblyInfo = new TestAssemblyInfo(typeof(TestAssemblyInfoTests).Assembly); _dummyMethodInfo = typeof(TestAssemblyInfoTests).GetMethods().First(); - var testContext = new Mock(); + var testContext = new Mock(); testContext.SetupGet(x => x.CancellationTokenSource).Returns(new CancellationTokenSource()); _testContext = testContext.Object; } @@ -152,7 +151,7 @@ public void RunAssemblyInitializeShouldThrowTestFailedExceptionOnAssertionFailur Verify(exception.StackTraceInformation!.ErrorStackTrace.StartsWith( " at Microsoft.VisualStudio.TestPlatform.MSTestAdapter.UnitTests.Execution.TestAssemblyInfoTests.<>c.", StringComparison.Ordinal)); #endif - Verify(exception.InnerException!.GetType() == typeof(UTF.AssertFailedException)); + Verify(exception.InnerException!.GetType() == typeof(AssertFailedException)); } public void RunAssemblyInitializeShouldThrowTestFailedExceptionWithInconclusiveOnAssertInconclusive() @@ -169,7 +168,7 @@ public void RunAssemblyInitializeShouldThrowTestFailedExceptionWithInconclusiveO Verify(exception.StackTraceInformation!.ErrorStackTrace.StartsWith( " at Microsoft.VisualStudio.TestPlatform.MSTestAdapter.UnitTests.Execution.TestAssemblyInfoTests.<>c.", StringComparison.Ordinal)); #endif - Verify(exception.InnerException!.GetType() == typeof(UTF.AssertInconclusiveException)); + Verify(exception.InnerException!.GetType() == typeof(AssertInconclusiveException)); } public void RunAssemblyInitializeShouldThrowTestFailedExceptionWithNonAssertExceptions() @@ -311,16 +310,16 @@ public void RunAssemblyCleanupShouldThrowTheInnerMostExceptionWhenThereAreMultip #endregion - [UTF.TestClass] + [TestClass] public class DummyTestClass { public static Action AssemblyInitializeMethodBody { get; set; } = null!; public static Action AssemblyCleanupMethodBody { get; set; } = null!; - public UTFExtension.TestContext TestContext { get; set; } = null!; + public TestContext TestContext { get; set; } = null!; - public static void AssemblyInitializeMethod(UTFExtension.TestContext testContext) => AssemblyInitializeMethodBody.Invoke(testContext); + public static void AssemblyInitializeMethod(TestContext testContext) => AssemblyInitializeMethodBody.Invoke(testContext); public static void AssemblyCleanupMethod() => AssemblyCleanupMethodBody.Invoke(); } diff --git a/test/UnitTests/MSTestAdapter.UnitTests/Execution/TestAssemblySettingsProviderTests.cs b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Execution/TestAssemblySettingsProviderTests.cs similarity index 91% rename from test/UnitTests/MSTestAdapter.UnitTests/Execution/TestAssemblySettingsProviderTests.cs rename to test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Execution/TestAssemblySettingsProviderTests.cs index 9d7754323d..328091f21f 100644 --- a/test/UnitTests/MSTestAdapter.UnitTests/Execution/TestAssemblySettingsProviderTests.cs +++ b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Execution/TestAssemblySettingsProviderTests.cs @@ -9,7 +9,7 @@ using TestFramework.ForTestingMSTest; -using UTF = Microsoft.VisualStudio.TestTools.UnitTesting; +using ExecutionScope = Microsoft.VisualStudio.TestTools.UnitTesting.ExecutionScope; namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.UnitTests.Execution; @@ -58,8 +58,8 @@ public void GetSettingsShouldSetParallelWorkers() .Returns(Assembly.GetExecutingAssembly()); _testablePlatformServiceProvider .MockReflectionOperations - .Setup(ro => ro.GetCustomAttributes(It.IsAny(), typeof(UTF.ParallelizeAttribute))) - .Returns([new UTF.ParallelizeAttribute { Workers = 10 }]); + .Setup(ro => ro.GetCustomAttributes(It.IsAny(), typeof(ParallelizeAttribute))) + .Returns([new ParallelizeAttribute { Workers = 10 }]); // Act. MSTest.TestAdapter.ObjectModel.TestAssemblySettings settings = new TestAssemblySettingsProvider().GetSettings("Foo"); @@ -77,8 +77,8 @@ public void GetSettingsShouldSetParallelWorkersToProcessorCountIfZero() .Returns(Assembly.GetExecutingAssembly()); _testablePlatformServiceProvider .MockReflectionOperations - .Setup(ro => ro.GetCustomAttributes(It.IsAny(), typeof(UTF.ParallelizeAttribute))) - .Returns([new UTF.ParallelizeAttribute { Workers = 0 }]); + .Setup(ro => ro.GetCustomAttributes(It.IsAny(), typeof(ParallelizeAttribute))) + .Returns([new ParallelizeAttribute { Workers = 0 }]); // Act. MSTest.TestAdapter.ObjectModel.TestAssemblySettings settings = new TestAssemblySettingsProvider().GetSettings("Foo"); @@ -111,8 +111,8 @@ public void GetSettingsShouldSetParallelScope() .Returns(Assembly.GetExecutingAssembly()); _testablePlatformServiceProvider .MockReflectionOperations - .Setup(ro => ro.GetCustomAttributes(It.IsAny(), typeof(UTF.ParallelizeAttribute))) - .Returns([new UTF.ParallelizeAttribute { Scope = ExecutionScope.MethodLevel }]); + .Setup(ro => ro.GetCustomAttributes(It.IsAny(), typeof(ParallelizeAttribute))) + .Returns([new ParallelizeAttribute { Scope = ExecutionScope.MethodLevel }]); // Act. MSTest.TestAdapter.ObjectModel.TestAssemblySettings settings = new TestAssemblySettingsProvider().GetSettings("Foo"); @@ -145,8 +145,8 @@ public void GetSettingsShouldSetCanParallelizeAssemblyToFalseIfDoNotParallelizeI .Returns(Assembly.GetExecutingAssembly()); _testablePlatformServiceProvider .MockReflectionOperations - .Setup(ro => ro.GetCustomAttributes(It.IsAny(), typeof(UTF.DoNotParallelizeAttribute))) - .Returns([new UTF.DoNotParallelizeAttribute()]); + .Setup(ro => ro.GetCustomAttributes(It.IsAny(), typeof(DoNotParallelizeAttribute))) + .Returns([new DoNotParallelizeAttribute()]); // Act. MSTest.TestAdapter.ObjectModel.TestAssemblySettings settings = new TestAssemblySettingsProvider().GetSettings("Foo"); diff --git a/test/UnitTests/MSTestAdapter.UnitTests/Execution/TestCaseDiscoverySinkTests.cs b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Execution/TestCaseDiscoverySinkTests.cs similarity index 100% rename from test/UnitTests/MSTestAdapter.UnitTests/Execution/TestCaseDiscoverySinkTests.cs rename to test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Execution/TestCaseDiscoverySinkTests.cs diff --git a/test/UnitTests/MSTestAdapter.UnitTests/Execution/TestClassInfoTests.cs b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Execution/TestClassInfoTests.cs similarity index 84% rename from test/UnitTests/MSTestAdapter.UnitTests/Execution/TestClassInfoTests.cs rename to test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Execution/TestClassInfoTests.cs index a079faea13..eaf89b0c38 100644 --- a/test/UnitTests/MSTestAdapter.UnitTests/Execution/TestClassInfoTests.cs +++ b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Execution/TestClassInfoTests.cs @@ -1,6 +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 System.Threading.Tasks; + using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution; using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel; using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices; @@ -11,15 +13,14 @@ using TestFramework.ForTestingMSTest; using UTF = Microsoft.VisualStudio.TestTools.UnitTesting; -using UTFExtension = Microsoft.VisualStudio.TestTools.UnitTesting; namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.UnitTests.Execution; public class TestClassInfoTests : TestContainer { - private sealed class TestTestCoxtextImpl : ITestContext + private sealed class TestTestContextImpl : ITestContext { - public TestTestCoxtextImpl(TestContext testContext) + public TestTestContextImpl(TestContext testContext) => Context = testContext; public TestContext Context { get; } @@ -55,11 +56,11 @@ public void ClearDiagnosticMessages() private readonly ConstructorInfo _testClassConstructor; - private readonly UTF.TestClassAttribute _testClassAttribute; + private readonly TestClassAttribute _testClassAttribute; private readonly TestAssemblyInfo _testAssemblyInfo; - private readonly UTFExtension.TestContext _testContext; + private readonly TestContext _testContext; private readonly TestClassInfo _testClassInfo; @@ -77,7 +78,7 @@ public TestClassInfoTests() _testClassAttribute, _testAssemblyInfo); - var testContext = new Mock(); + var testContext = new Mock(); testContext.SetupGet(x => x.CancellationTokenSource).Returns(new CancellationTokenSource()); _testContext = testContext.Object; @@ -124,7 +125,7 @@ void Action() VerifyThrows(Action); } - public void TestClassInfoClassCleanupMethodShouldNotInvokeWhenNoTestClassInitializedIsCalled() + public async Task TestClassInfoClassCleanupMethodShouldNotInvokeWhenNoTestClassInitializedIsCalled() { int classCleanupCallCount = 0; DummyTestClass.ClassCleanupMethodBody = () => classCleanupCallCount++; @@ -132,12 +133,12 @@ public void TestClassInfoClassCleanupMethodShouldNotInvokeWhenNoTestClassInitial _testClassInfo.ClassCleanupMethod = typeof(DummyTestClass).GetMethod("ClassCleanupMethod")!; _testClassInfo.ClassInitializeMethod = typeof(DummyTestClass).GetMethod("ClassInitializeMethod")!; - TestFailedException? ex = _testClassInfo.ExecuteClassCleanup(new TestContextImplementation(null, new StringWriter(), new Dictionary()), out _); // call cleanup without calling init + TestFailedException? ex = await _testClassInfo.ExecuteClassCleanupAsync(new TestContextImplementation(null, new StringWriter(), new Dictionary())); // call cleanup without calling init Verify(ex is null); Verify(classCleanupCallCount == 0); } - public void TestClassInfoClassCleanupMethodShouldInvokeWhenTestClassInitializedIsCalled() + public async Task TestClassInfoClassCleanupMethodShouldInvokeWhenTestClassInitializedIsCalled() { int classCleanupCallCount = 0; DummyTestClass.ClassCleanupMethodBody = () => classCleanupCallCount++; @@ -146,13 +147,13 @@ public void TestClassInfoClassCleanupMethodShouldInvokeWhenTestClassInitializedI _testClassInfo.ClassInitializeMethod = typeof(DummyTestClass).GetMethod("ClassInitializeMethod")!; GetResultOrRunClassInitialize(); - TestFailedException? ex = _testClassInfo.ExecuteClassCleanup(new TestContextImplementation(null, new StringWriter(), new Dictionary()), out _); // call cleanup without calling init + TestFailedException? ex = await _testClassInfo.ExecuteClassCleanupAsync(new TestContextImplementation(null, new StringWriter(), new Dictionary())); // call cleanup without calling init Verify(ex is null); Verify(classCleanupCallCount == 1); } - public void TestClassInfoClassCleanupMethodShouldInvokeBaseClassCleanupMethodWhenTestClassInitializedIsCalled() + public async Task TestClassInfoClassCleanupMethodShouldInvokeBaseClassCleanupMethodWhenTestClassInitializedIsCalled() { int classCleanupCallCount = 0; DummyBaseTestClass.ClassCleanupMethodBody = () => classCleanupCallCount++; @@ -161,7 +162,7 @@ public void TestClassInfoClassCleanupMethodShouldInvokeBaseClassCleanupMethodWhe _testClassInfo.BaseClassCleanupMethods.Add(typeof(DummyBaseTestClass).GetMethod("CleanupClassMethod")!); GetResultOrRunClassInitialize(); - TestFailedException? ex = _testClassInfo.ExecuteClassCleanup(new TestContextImplementation(null, new StringWriter(), new Dictionary()), out _); + TestFailedException? ex = await _testClassInfo.ExecuteClassCleanupAsync(new TestContextImplementation(null, new StringWriter(), new Dictionary())); Verify(ex is null); Verify(classCleanupCallCount == 1); @@ -363,7 +364,7 @@ public void RunClassInitializeShouldThrowTestFailedExceptionOnAssertionFailure() Verify(exception.StackTraceInformation!.ErrorStackTrace.StartsWith( " at Microsoft.VisualStudio.TestPlatform.MSTestAdapter.UnitTests.Execution.TestClassInfoTests.<>c.", StringComparison.Ordinal)); #endif - Verify(exception.InnerException!.GetType() == typeof(UTF.AssertFailedException)); + Verify(exception.InnerException!.GetType() == typeof(AssertFailedException)); } public void RunClassInitializeShouldThrowTestFailedExceptionWithInconclusiveOnAssertInconclusive() @@ -382,7 +383,7 @@ public void RunClassInitializeShouldThrowTestFailedExceptionWithInconclusiveOnAs Verify(exception.StackTraceInformation!.ErrorStackTrace.StartsWith( " at Microsoft.VisualStudio.TestPlatform.MSTestAdapter.UnitTests.Execution.TestClassInfoTests.<>c.", StringComparison.Ordinal)); #endif - Verify(exception.InnerException!.GetType() == typeof(UTF.AssertInconclusiveException)); + Verify(exception.InnerException!.GetType() == typeof(AssertInconclusiveException)); } public void RunClassInitializeShouldThrowTestFailedExceptionWithNonAssertExceptions() @@ -451,12 +452,12 @@ private TestResult GetResultOrRunClassInitialize() => GetResultOrRunClassInitialize(_testContext); private TestResult GetResultOrRunClassInitialize(TestContext? testContext) - => _testClassInfo.GetResultOrRunClassInitialize(new TestTestCoxtextImpl(testContext!), string.Empty, string.Empty, string.Empty, string.Empty); + => _testClassInfo.GetResultOrRunClassInitializeAsync(new TestTestContextImpl(testContext!), string.Empty, string.Empty, string.Empty, string.Empty).GetAwaiter().GetResult(); #endregion #region Run Class Cleanup tests - public void RunClassCleanupShouldInvokeIfClassCleanupMethod() + public async Task RunClassCleanupShouldInvokeIfClassCleanupMethod() { // Arrange int classCleanupCallCount = 0; @@ -465,14 +466,14 @@ public void RunClassCleanupShouldInvokeIfClassCleanupMethod() // Act GetResultOrRunClassInitialize(null); - TestFailedException? ex = _testClassInfo.ExecuteClassCleanup(new TestContextImplementation(null, new StringWriter(), new Dictionary()), out _); + TestFailedException? ex = await _testClassInfo.ExecuteClassCleanupAsync(new TestContextImplementation(null, new StringWriter(), new Dictionary())); // Assert Verify(ex is null); Verify(classCleanupCallCount == 1); } - public void RunClassCleanupShouldNotInvokeIfClassCleanupIsNull() + public async Task RunClassCleanupShouldNotInvokeIfClassCleanupIsNull() { // Arrange int classCleanupCallCount = 0; @@ -480,14 +481,14 @@ public void RunClassCleanupShouldNotInvokeIfClassCleanupIsNull() _testClassInfo.ClassCleanupMethod = null; // Act - TestFailedException? ex = _testClassInfo.ExecuteClassCleanup(new TestContextImplementation(null, new StringWriter(), new Dictionary()), out _); + TestFailedException? ex = await _testClassInfo.ExecuteClassCleanupAsync(new TestContextImplementation(null, new StringWriter(), new Dictionary())); // Assert Verify(ex is null); Verify(classCleanupCallCount == 0); } - public void RunClassCleanupShouldReturnAssertFailureExceptionDetails() + public async Task RunClassCleanupShouldReturnAssertFailureExceptionDetails() { // Arrange DummyTestClass.ClassCleanupMethodBody = () => UTF.Assert.Fail("Test Failure"); @@ -495,7 +496,7 @@ public void RunClassCleanupShouldReturnAssertFailureExceptionDetails() // Act GetResultOrRunClassInitialize(null); - TestFailedException? classCleanupException = _testClassInfo.ExecuteClassCleanup(new TestContextImplementation(null, new StringWriter(), new Dictionary()), out _); + TestFailedException? classCleanupException = await _testClassInfo.ExecuteClassCleanupAsync(new TestContextImplementation(null, new StringWriter(), new Dictionary())); // Assert Verify(classCleanupException is not null); @@ -509,7 +510,7 @@ public void RunClassCleanupShouldReturnAssertFailureExceptionDetails() #endif } - public void RunClassCleanupShouldReturnAssertInconclusiveExceptionDetails() + public async Task RunClassCleanupShouldReturnAssertInconclusiveExceptionDetails() { // Arrange DummyTestClass.ClassCleanupMethodBody = () => UTF.Assert.Inconclusive("Test Inconclusive"); @@ -517,7 +518,7 @@ public void RunClassCleanupShouldReturnAssertInconclusiveExceptionDetails() // Act GetResultOrRunClassInitialize(null); - TestFailedException? classCleanupException = _testClassInfo.ExecuteClassCleanup(new TestContextImplementation(null, new StringWriter(), new Dictionary()), out _); + TestFailedException? classCleanupException = await _testClassInfo.ExecuteClassCleanupAsync(new TestContextImplementation(null, new StringWriter(), new Dictionary())); // Assert Verify(classCleanupException is not null); @@ -530,7 +531,7 @@ public void RunClassCleanupShouldReturnAssertInconclusiveExceptionDetails() #endif } - public void RunClassCleanupShouldReturnExceptionDetailsOfNonAssertExceptions() + public async Task RunClassCleanupShouldReturnExceptionDetailsOfNonAssertExceptions() { // Arrange DummyTestClass.ClassCleanupMethodBody = () => throw new ArgumentException("Argument Exception"); @@ -538,7 +539,7 @@ public void RunClassCleanupShouldReturnExceptionDetailsOfNonAssertExceptions() // Act GetResultOrRunClassInitialize(null); - TestFailedException? classCleanupException = _testClassInfo.ExecuteClassCleanup(new TestContextImplementation(null, new StringWriter(), new Dictionary()), out _); + TestFailedException? classCleanupException = await _testClassInfo.ExecuteClassCleanupAsync(new TestContextImplementation(null, new StringWriter(), new Dictionary())); // Assert Verify(classCleanupException is not null); @@ -547,7 +548,7 @@ public void RunClassCleanupShouldReturnExceptionDetailsOfNonAssertExceptions() Verify(classCleanupException.Message.Contains($"{typeof(TestClassInfoTests).FullName}.<>c.<{nameof(this.RunClassCleanupShouldReturnExceptionDetailsOfNonAssertExceptions)}>")); } - public void RunBaseClassCleanupWithNoDerivedClassCleanupShouldReturnExceptionDetailsOfNonAssertExceptions() + public async Task RunBaseClassCleanupWithNoDerivedClassCleanupShouldReturnExceptionDetailsOfNonAssertExceptions() { // Arrange DummyBaseTestClass.ClassCleanupMethodBody = () => throw new ArgumentException("Argument Exception"); @@ -557,7 +558,7 @@ public void RunBaseClassCleanupWithNoDerivedClassCleanupShouldReturnExceptionDet // Act GetResultOrRunClassInitialize(null); - TestFailedException? classCleanupException = _testClassInfo.ExecuteClassCleanup(new TestContextImplementation(null, new StringWriter(), new Dictionary()), out _); + TestFailedException? classCleanupException = await _testClassInfo.ExecuteClassCleanupAsync(new TestContextImplementation(null, new StringWriter(), new Dictionary())); // Assert Verify(classCleanupException is not null); @@ -566,7 +567,7 @@ public void RunBaseClassCleanupWithNoDerivedClassCleanupShouldReturnExceptionDet Verify(classCleanupException.Message.Contains($"{typeof(TestClassInfoTests).FullName}.<>c.<{nameof(this.RunBaseClassCleanupWithNoDerivedClassCleanupShouldReturnExceptionDetailsOfNonAssertExceptions)}>")); } - public void RunBaseClassCleanupEvenIfThereIsNoDerivedClassCleanup() + public async Task RunBaseClassCleanupEvenIfThereIsNoDerivedClassCleanup() { // Arrange int classCleanupCallCount = 0; @@ -576,7 +577,7 @@ public void RunBaseClassCleanupEvenIfThereIsNoDerivedClassCleanup() _testClassInfo.BaseClassCleanupMethods.Add(baseClassCleanupMethod); // Act - TestFailedException? ex = _testClassInfo.ExecuteClassCleanup(new TestContextImplementation(null, new StringWriter(), new Dictionary()), out _); + TestFailedException? ex = await _testClassInfo.ExecuteClassCleanupAsync(new TestContextImplementation(null, new StringWriter(), new Dictionary())); // Assert Verify(ex is null); @@ -585,7 +586,7 @@ public void RunBaseClassCleanupEvenIfThereIsNoDerivedClassCleanup() // Act 2 GetResultOrRunClassInitialize(null); - ex = _testClassInfo.ExecuteClassCleanup(new TestContextImplementation(null, new StringWriter(), new Dictionary()), out _); + ex = await _testClassInfo.ExecuteClassCleanupAsync(new TestContextImplementation(null, new StringWriter(), new Dictionary())); // Assert 2 Verify(ex is null); @@ -594,7 +595,7 @@ public void RunBaseClassCleanupEvenIfThereIsNoDerivedClassCleanup() Verify(classCleanupCallCount == 1, "DummyBaseTestClass.CleanupClassMethod call count"); // Act 3 - ex = _testClassInfo.ExecuteClassCleanup(new TestContextImplementation(null, new StringWriter(), new Dictionary()), out _); + ex = await _testClassInfo.ExecuteClassCleanupAsync(new TestContextImplementation(null, new StringWriter(), new Dictionary())); // Assert 3 Verify(ex is null); @@ -602,7 +603,7 @@ public void RunBaseClassCleanupEvenIfThereIsNoDerivedClassCleanup() Verify(classCleanupCallCount == 1, "DummyBaseTestClass.CleanupClassMethod call count"); } - public void RunClassCleanupShouldThrowTheInnerMostExceptionWhenThereAreMultipleNestedTypeInitializationExceptions() + public async Task RunClassCleanupShouldThrowTheInnerMostExceptionWhenThereAreMultipleNestedTypeInitializationExceptions() { // This helper calls inner helper, and the inner helper ctor throws. // We want to see the real exception on screen, and not TypeInitializationException @@ -611,7 +612,7 @@ public void RunClassCleanupShouldThrowTheInnerMostExceptionWhenThereAreMultipleN _testClassInfo.ClassCleanupMethod = typeof(DummyTestClass).GetMethod("ClassCleanupMethod")!; GetResultOrRunClassInitialize(null); - TestFailedException? classCleanupException = _testClassInfo.ExecuteClassCleanup(new TestContextImplementation(null, new StringWriter(), new Dictionary()), out _); + TestFailedException? classCleanupException = await _testClassInfo.ExecuteClassCleanupAsync(new TestContextImplementation(null, new StringWriter(), new Dictionary())); Verify(classCleanupException is not null); Verify(classCleanupException.Message.StartsWith("Class Cleanup method DummyTestClass.ClassCleanupMethod failed. Error Message: System.InvalidOperationException: I fail..", StringComparison.Ordinal)); @@ -627,10 +628,10 @@ public class DummyGrandParentTestClass public static Action CleanupClassMethodBody { get; set; } = null!; - public UTFExtension.TestContext BaseTestContext { get; set; } = null!; + public TestContext BaseTestContext { get; set; } = null!; - [UTF.ClassInitialize(UTF.InheritanceBehavior.BeforeEachDerivedClass)] - public static void InitClassMethod(UTFExtension.TestContext testContext) => ClassInitMethodBody?.Invoke(testContext); + [ClassInitialize(UTF.InheritanceBehavior.BeforeEachDerivedClass)] + public static void InitClassMethod(TestContext testContext) => ClassInitMethodBody?.Invoke(testContext); public static void ClassCleanupMethod() => CleanupClassMethodBody?.Invoke(); } @@ -642,10 +643,10 @@ public class DummyBaseTestClass : DummyGrandParentTestClass public static Action ClassCleanupMethodBody { get; set; } = null!; - public UTFExtension.TestContext TestContext { get; set; } = null!; + public TestContext TestContext { get; set; } = null!; - [UTF.ClassInitialize(UTF.InheritanceBehavior.BeforeEachDerivedClass)] - public static void InitBaseClassMethod(UTFExtension.TestContext testContext) => ClassInitializeMethodBody?.Invoke(testContext); + [ClassInitialize(UTF.InheritanceBehavior.BeforeEachDerivedClass)] + public static void InitBaseClassMethod(TestContext testContext) => ClassInitializeMethodBody?.Invoke(testContext); public static void CleanupClassMethod() => ClassCleanupMethodBody?.Invoke(); } @@ -657,9 +658,9 @@ public class DummyDerivedTestClass : DummyBaseTestClass public static Action DerivedClassCleanupMethodBody { get; set; } = null!; - public UTFExtension.TestContext Context { get; set; } = null!; + public TestContext Context { get; set; } = null!; - public static void InitDerivedClassMethod(UTFExtension.TestContext testContext) => DerivedClassInitializeMethodBody?.Invoke(testContext); + public static void InitDerivedClassMethod(TestContext testContext) => DerivedClassInitializeMethodBody?.Invoke(testContext); public static void CleanupDerivedClassMethod() => DerivedClassCleanupMethodBody?.Invoke(); } @@ -671,14 +672,14 @@ public class DummyTestClass public static Action ClassCleanupMethodBody { get; set; } = null!; - public UTFExtension.TestContext TestContext { get; set; } = null!; + public TestContext TestContext { get; set; } = null!; - public static void ClassInitializeMethod(UTFExtension.TestContext testContext) => ClassInitializeMethodBody?.Invoke(testContext); + public static void ClassInitializeMethod(TestContext testContext) => ClassInitializeMethodBody?.Invoke(testContext); public static void ClassCleanupMethod() => ClassCleanupMethodBody?.Invoke(); } - private class DummyTestClassAttribute : UTF.TestClassAttribute; + private class DummyTestClassAttribute : TestClassAttribute; private static class FailingStaticHelper { diff --git a/test/UnitTests/MSTestAdapter.UnitTests/Execution/TestExecutionManagerTests.cs b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Execution/TestExecutionManagerTests.cs similarity index 96% rename from test/UnitTests/MSTestAdapter.UnitTests/Execution/TestExecutionManagerTests.cs rename to test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Execution/TestExecutionManagerTests.cs index cea2718a3b..7c10d74213 100644 --- a/test/UnitTests/MSTestAdapter.UnitTests/Execution/TestExecutionManagerTests.cs +++ b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Execution/TestExecutionManagerTests.cs @@ -3,7 +3,6 @@ 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.Discovery; @@ -16,7 +15,7 @@ using TestFramework.ForTestingMSTest; -using TestAdapterConstants = Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Constants; +using ExecutionScope = Microsoft.VisualStudio.TestTools.UnitTesting.ExecutionScope; using TestResult = Microsoft.VisualStudio.TestPlatform.ObjectModel.TestResult; namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.UnitTests.Execution; @@ -30,25 +29,25 @@ public class TestExecutionManagerTests : TestContainer private readonly TestProperty[] _tcmKnownProperties = [ - TestAdapterConstants.TestRunIdProperty, - TestAdapterConstants.TestPlanIdProperty, - TestAdapterConstants.BuildConfigurationIdProperty, - TestAdapterConstants.BuildDirectoryProperty, - TestAdapterConstants.BuildFlavorProperty, - TestAdapterConstants.BuildNumberProperty, - TestAdapterConstants.BuildPlatformProperty, - TestAdapterConstants.BuildUriProperty, - TestAdapterConstants.TfsServerCollectionUrlProperty, - TestAdapterConstants.TfsTeamProjectProperty, - TestAdapterConstants.IsInLabEnvironmentProperty, - TestAdapterConstants.TestCaseIdProperty, - TestAdapterConstants.TestConfigurationIdProperty, - TestAdapterConstants.TestConfigurationNameProperty, - TestAdapterConstants.TestPointIdProperty, + EngineConstants.TestRunIdProperty, + EngineConstants.TestPlanIdProperty, + EngineConstants.BuildConfigurationIdProperty, + EngineConstants.BuildDirectoryProperty, + EngineConstants.BuildFlavorProperty, + EngineConstants.BuildNumberProperty, + EngineConstants.BuildPlatformProperty, + EngineConstants.BuildUriProperty, + EngineConstants.TfsServerCollectionUrlProperty, + EngineConstants.TfsTeamProjectProperty, + EngineConstants.IsInLabEnvironmentProperty, + EngineConstants.TestCaseIdProperty, + EngineConstants.TestConfigurationIdProperty, + EngineConstants.TestConfigurationNameProperty, + EngineConstants.TestPointIdProperty, ]; private TestableRunContextTestExecutionTests _runContext; - private List _callers = new(); + private List _callers = []; private int _enqueuedParallelTestsCount; public TestExecutionManagerTests() @@ -666,8 +665,8 @@ public async Task RunTestsForTestShouldRunNonParallelizableTestsSeparately() TestCase testCase3 = GetTestCase(typeof(DummyTestClassWithDoNotParallelizeMethods), "TestMethod3"); TestCase testCase4 = GetTestCase(typeof(DummyTestClassWithDoNotParallelizeMethods), "TestMethod4"); - testCase3.SetPropertyValue(TestAdapterConstants.DoNotParallelizeProperty, true); - testCase4.SetPropertyValue(TestAdapterConstants.DoNotParallelizeProperty, true); + testCase3.SetPropertyValue(EngineConstants.DoNotParallelizeProperty, true); + testCase4.SetPropertyValue(EngineConstants.DoNotParallelizeProperty, true); TestCase[] tests = [testCase1, testCase2, testCase3, testCase4]; _runContext.MockRunSettings.Setup(rs => rs.SettingsXml).Returns( @@ -776,7 +775,7 @@ private async Task RunTestsForTestShouldRunTestsInTheParentDomainsApartmentState TestCase testCase3 = GetTestCase(typeof(DummyTestClassWithDoNotParallelizeMethods), "TestMethod3"); TestCase testCase4 = GetTestCase(typeof(DummyTestClassWithDoNotParallelizeMethods), "TestMethod4"); - testCase4.SetPropertyValue(TestAdapterConstants.DoNotParallelizeProperty, true); + testCase4.SetPropertyValue(EngineConstants.DoNotParallelizeProperty, true); TestCase[] tests = [testCase1, testCase2, testCase3, testCase4]; _runContext.MockRunSettings.Setup(rs => rs.SettingsXml).Returns( @@ -861,9 +860,9 @@ private void VerifyTcmProperties(IDictionary? tcmProperties, Tes private void SetTestCaseProperties(TestCase testCase, object[] propertiesValue) { - System.Collections.IEnumerator tcmKnownPropertiesEnumerator = _tcmKnownProperties.GetEnumerator(); + IEnumerator tcmKnownPropertiesEnumerator = _tcmKnownProperties.GetEnumerator(); - System.Collections.IEnumerator propertiesValueEnumerator = propertiesValue.GetEnumerator(); + IEnumerator propertiesValueEnumerator = propertiesValue.GetEnumerator(); while (tcmKnownPropertiesEnumerator.MoveNext() && propertiesValueEnumerator.MoveNext()) { object? property = tcmKnownPropertiesEnumerator.Current; @@ -1119,7 +1118,7 @@ internal sealed class TestableTestCaseFilterExpression : ITestCaseFilterExpressi public TestableTestCaseFilterExpression(Func matchTestCase) => _matchTest = matchTestCase; - public string TestCaseFilterValue { get; } = null!; + public string TestCaseFilterValue => null!; public bool MatchTestCase(TestCase testCase, Func propertyValueProvider) => _matchTest(testCase); } diff --git a/test/UnitTests/MSTestAdapter.UnitTests/Execution/TestMethodFilterTests.cs b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Execution/TestMethodFilterTests.cs similarity index 92% rename from test/UnitTests/MSTestAdapter.UnitTests/Execution/TestMethodFilterTests.cs rename to test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Execution/TestMethodFilterTests.cs index e416447e4b..28f5710903 100644 --- a/test/UnitTests/MSTestAdapter.UnitTests/Execution/TestMethodFilterTests.cs +++ b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Execution/TestMethodFilterTests.cs @@ -2,15 +2,13 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter; +using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices; using Microsoft.VisualStudio.TestPlatform.ObjectModel; using Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter; using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging; using TestFramework.ForTestingMSTest; -using UTF = Microsoft.VisualStudio.TestTools.UnitTesting; -using UTFExtension = Microsoft.VisualStudio.TestTools.UnitTesting; - namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.UnitTests.Execution; public class TestMethodFilterTests : TestContainer @@ -59,7 +57,7 @@ public void PropertyProviderValueForInvalidPropertyNameReturnsNull() { Type type = typeof(DummyTestClassWithTestMethods); string fullName = $"{type.FullName}.TestMethod"; - TestCase testCase = new(fullName, MSTest.TestAdapter.Constants.ExecutorUri, Assembly.GetExecutingAssembly().FullName!); + TestCase testCase = new(fullName, EngineConstants.ExecutorUri, Assembly.GetExecutingAssembly().FullName!); object? result = _testMethodFilter.PropertyValueProvider(testCase, null); Verify(result is null); @@ -70,7 +68,7 @@ public void PropertyProviderValueForSupportedPropertyNameWhichIsNotSetReturnsNul Type type = typeof(DummyTestClassWithTestMethods); string fullName = $"{type.FullName}.TestMethod"; - TestCase testCase = new(fullName, MSTest.TestAdapter.Constants.ExecutorUri, Assembly.GetExecutingAssembly().FullName!); + TestCase testCase = new(fullName, EngineConstants.ExecutorUri, Assembly.GetExecutingAssembly().FullName!); object? result = _testMethodFilter.PropertyValueProvider(testCase, "Priority"); Verify(result is null); } @@ -80,7 +78,7 @@ public void PropertyProviderValueForValidTestAndSupportedPropertyNameReturnsValu Type type = typeof(DummyTestClassWithTestMethods); string fullName = $"{type.FullName}.TestMethod"; - TestCase testCase = new(fullName, MSTest.TestAdapter.Constants.ExecutorUri, Assembly.GetExecutingAssembly().FullName!); + TestCase testCase = new(fullName, EngineConstants.ExecutorUri, Assembly.GetExecutingAssembly().FullName!); object? result = _testMethodFilter.PropertyValueProvider(testCase, "FullyQualifiedName"); Verify(fullName.Equals(result)); @@ -163,9 +161,9 @@ public void GetFilterExpressionForDiscoveryContextWithGetTestCaseFilterThrowingE [DummyTestClass] internal class DummyTestClassWithTestMethods { - public UTFExtension.TestContext TestContext { get; set; } = null!; + public TestContext TestContext { get; set; } = null!; - [UTF.TestMethod] + [TestMethod] public void TestMethod() { } @@ -227,10 +225,10 @@ private class TestableDiscoveryContextWithoutGetTestCaseFilter : IDiscoveryConte private sealed class TestableTestCaseFilterExpression : ITestCaseFilterExpression { - public string TestCaseFilterValue { get; } = null!; + public string TestCaseFilterValue => null!; public bool MatchTestCase(TestCase testCase, Func propertyValueProvider) => throw new NotImplementedException(); } - private class DummyTestClassAttribute : UTF.TestClassAttribute; + private class DummyTestClassAttribute : TestClassAttribute; } diff --git a/test/UnitTests/MSTestAdapter.UnitTests/Execution/TestMethodInfoTests.cs b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Execution/TestMethodInfoTests.cs similarity index 81% rename from test/UnitTests/MSTestAdapter.UnitTests/Execution/TestMethodInfoTests.cs rename to test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Execution/TestMethodInfoTests.cs index b935c23c3b..73f68819ac 100644 --- a/test/UnitTests/MSTestAdapter.UnitTests/Execution/TestMethodInfoTests.cs +++ b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Execution/TestMethodInfoTests.cs @@ -6,15 +6,14 @@ using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel; using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices; using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface; +using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Resources; using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.UnitTests.TestableImplementations; using Moq; using TestFramework.ForTestingMSTest; -using Resource = Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Resource; using UTF = Microsoft.VisualStudio.TestTools.UnitTesting; -using UTFExtension = Microsoft.VisualStudio.TestTools.UnitTesting; namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.UnitTests.Execution; @@ -27,9 +26,9 @@ public class TestMethodInfoTests : TestContainer private readonly MethodInfo _methodInfo; - private readonly UTF.TestClassAttribute _classAttribute; + private readonly TestClassAttribute _classAttribute; - private readonly UTF.TestMethodAttribute _testMethodAttribute; + private readonly TestMethodAttribute _testMethodAttribute; private readonly TestAssemblyInfo _testAssemblyInfo; @@ -39,20 +38,20 @@ public class TestMethodInfoTests : TestContainer private readonly TestClassInfo _testClassInfo; - private readonly UTF.ExpectedExceptionAttribute _expectedException; + private readonly ExpectedExceptionAttribute _expectedException; public TestMethodInfoTests() { _constructorInfo = typeof(DummyTestClass).GetConstructor([])!; _methodInfo = typeof(DummyTestClass).GetMethods().Single(m => m.Name.Equals("DummyTestMethod", StringComparison.Ordinal)); - _classAttribute = new UTF.TestClassAttribute(); - _testMethodAttribute = new UTF.TestMethodAttribute(); + _classAttribute = new TestClassAttribute(); + _testMethodAttribute = new TestMethodAttribute(); _testAssemblyInfo = new TestAssemblyInfo(typeof(DummyTestClass).Assembly); var testMethod = new TestMethod("dummyTestName", "dummyClassName", "dummyAssemblyName", false); _testContextImplementation = new TestContextImplementation(testMethod, new ThreadSafeStringWriter(null!, "test"), new Dictionary()); _testClassInfo = new TestClassInfo(typeof(DummyTestClass), _constructorInfo, true, _classAttribute, _testAssemblyInfo); - _expectedException = new UTF.ExpectedExceptionAttribute(typeof(DivideByZeroException)); + _expectedException = new ExpectedExceptionAttribute(typeof(DivideByZeroException)); _testMethodInfo = new TestMethodInfo( _methodInfo, @@ -84,7 +83,7 @@ public void SetArgumentsShouldSetArgumentsNeededForCurrentTestRun() #region TestMethod invoke scenarios - public void TestMethodInfoInvokeShouldWaitForAsyncTestMethodsToComplete() + public async Task TestMethodInfoInvokeShouldWaitForAsyncTestMethodsToComplete() { bool methodCalled = false; DummyTestClass.DummyAsyncTestMethodBody = () => Task.Run(() => methodCalled = true); @@ -96,15 +95,15 @@ public void TestMethodInfoInvokeShouldWaitForAsyncTestMethodsToComplete() Executor = _testMethodAttribute, }; - UTF.TestResult result = method.Invoke(null); + TestResult result = await method.InvokeAsync(null); Verify(methodCalled); Verify(result.Outcome == UTF.UnitTestOutcome.Passed); } - public void TestMethodInfoInvokeAsyncShouldHandleThrowAssertInconclusive() + public async Task TestMethodInfoInvokeAsyncShouldHandleThrowAssertInconclusive() { - DummyTestClass.DummyAsyncTestMethodBody = () => Task.Run(() => throw new UTF.AssertInconclusiveException()); + DummyTestClass.DummyAsyncTestMethodBody = () => Task.Run(() => throw new AssertInconclusiveException()); MethodInfo asyncMethodInfo = typeof(DummyTestClass).GetMethod("DummyAsyncTestMethod")!; var method = new TestMethodInfo( @@ -116,12 +115,12 @@ public void TestMethodInfoInvokeAsyncShouldHandleThrowAssertInconclusive() Executor = _testMethodAttribute, }; - UTF.TestResult result = method.Invoke(null); + TestResult result = await method.InvokeAsync(null); Verify(result.Outcome == UTF.UnitTestOutcome.Inconclusive); } - public void TestMethodInfoInvokeAsyncShouldHandleAssertInconclusive() + public async Task TestMethodInfoInvokeAsyncShouldHandleAssertInconclusive() { DummyTestClass.DummyAsyncTestMethodBody = () => Task.Run(() => UTF.Assert.Inconclusive()); MethodInfo asyncMethodInfo = typeof(DummyTestClass).GetMethod("DummyAsyncTestMethod")!; @@ -135,14 +134,14 @@ public void TestMethodInfoInvokeAsyncShouldHandleAssertInconclusive() Executor = _testMethodAttribute, }; - UTF.TestResult result = method.Invoke(null); + TestResult result = await method.InvokeAsync(null); Verify(result.Outcome == UTF.UnitTestOutcome.Inconclusive); } - public void TestMethodInfoInvokeShouldHandleThrowAssertInconclusive() + public async Task TestMethodInfoInvokeShouldHandleThrowAssertInconclusive() { - DummyTestClass.TestMethodBody = d => throw new UTF.AssertInconclusiveException(); + DummyTestClass.TestMethodBody = d => throw new AssertInconclusiveException(); MethodInfo dummyMethodInfo = typeof(DummyTestClass).GetMethod("DummyTestMethod")!; var method = new TestMethodInfo( @@ -154,12 +153,12 @@ public void TestMethodInfoInvokeShouldHandleThrowAssertInconclusive() Executor = _testMethodAttribute, }; - UTF.TestResult result = method.Invoke(null); + TestResult result = await method.InvokeAsync(null); Verify(result.Outcome == UTF.UnitTestOutcome.Inconclusive); } - public void TestMethodInfoInvokeShouldHandleAssertInconclusive() + public async Task TestMethodInfoInvokeShouldHandleAssertInconclusive() { DummyTestClass.TestMethodBody = d => UTF.Assert.Inconclusive(); MethodInfo dummyMethodInfo = typeof(DummyTestClass).GetMethod("DummyTestMethod")!; @@ -173,12 +172,12 @@ public void TestMethodInfoInvokeShouldHandleAssertInconclusive() Executor = _testMethodAttribute, }; - UTF.TestResult result = method.Invoke(null); + TestResult result = await method.InvokeAsync(null); Verify(result.Outcome == UTF.UnitTestOutcome.Inconclusive); } - public void TestMethodInfoInvokeShouldReportTestContextMessages() + public async Task TestMethodInfoInvokeShouldReportTestContextMessages() { DummyTestClass.TestMethodBody = o => _testContextImplementation.WriteLine("TestContext"); @@ -191,12 +190,12 @@ public void TestMethodInfoInvokeShouldReportTestContextMessages() Executor = _testMethodAttribute, }; - UTF.TestResult result = method.Invoke(null); + TestResult result = await method.InvokeAsync(null); Verify(result.TestContextMessages!.Contains("TestContext")); } - public void TestMethodInfoInvokeShouldClearTestContextMessagesAfterReporting() + public async Task TestMethodInfoInvokeShouldClearTestContextMessagesAfterReporting() { DummyTestClass.TestMethodBody = o => _testContextImplementation.WriteLine("TestContext"); @@ -209,18 +208,18 @@ public void TestMethodInfoInvokeShouldClearTestContextMessagesAfterReporting() Executor = _testMethodAttribute, }; - UTF.TestResult result = method.Invoke(null); + TestResult result = await method.InvokeAsync(null); Verify(result.TestContextMessages!.Contains("TestContext")); DummyTestClass.TestMethodBody = o => _testContextImplementation.WriteLine("SeaShore"); - result = method.Invoke(null); + result = await method.InvokeAsync(null); Verify(result.TestContextMessages!.Contains("SeaShore")); } - public void Invoke_WhenTestMethodThrowsMissingMethodException_TestOutcomeIsFailedAndExceptionIsPreserved() + public async Task Invoke_WhenTestMethodThrowsMissingMethodException_TestOutcomeIsFailedAndExceptionIsPreserved() { DummyTestClass.TestMethodBody = _ => { @@ -242,7 +241,7 @@ public void Invoke_WhenTestMethodThrowsMissingMethodException_TestOutcomeIsFaile Executor = _testMethodAttribute, }; - UTF.TestResult result = method.Invoke(null); + TestResult result = await method.InvokeAsync(null); Verify(result.Outcome == UTF.UnitTestOutcome.Failed); Verify(result.TestFailureException is TestFailedException); @@ -253,32 +252,32 @@ public void Invoke_WhenTestMethodThrowsMissingMethodException_TestOutcomeIsFaile #region TestClass constructor setup - public void TestMethodInfoInvokeShouldCreateNewInstanceOfTestClassOnEveryCall() + public async Task TestMethodInfoInvokeShouldCreateNewInstanceOfTestClassOnEveryCall() { int ctorCallCount = 0; DummyTestClass.TestConstructorMethodBody = () => ctorCallCount++; - UTF.TestResult result = _testMethodInfo.Invoke(null); - _testMethodInfo.Invoke(null); + TestResult result = await _testMethodInfo.InvokeAsync(null); + await _testMethodInfo.InvokeAsync(null); Verify(result.Outcome == UTF.UnitTestOutcome.Passed); Verify(ctorCallCount == 2); } - public void TestMethodInfoInvokeShouldMarkOutcomeFailedIfTestClassConstructorThrows() + public async Task TestMethodInfoInvokeShouldMarkOutcomeFailedIfTestClassConstructorThrows() { DummyTestClass.TestConstructorMethodBody = () => throw new NotImplementedException(); - UTF.TestResult result = _testMethodInfo.Invoke(null); + TestResult result = await _testMethodInfo.InvokeAsync(null); Verify(result.Outcome == UTF.UnitTestOutcome.Failed); } - public void TestMethodInfoInvokeShouldSetErrorMessageIfTestClassConstructorThrows() + public async Task TestMethodInfoInvokeShouldSetErrorMessageIfTestClassConstructorThrows() { DummyTestClass.TestConstructorMethodBody = () => throw new NotImplementedException("dummyExceptionMessage"); - UTF.TestResult result = _testMethodInfo.Invoke(null); + TestResult result = await _testMethodInfo.InvokeAsync(null); string errorMessage = string.Format( CultureInfo.InvariantCulture, @@ -288,7 +287,7 @@ public void TestMethodInfoInvokeShouldSetErrorMessageIfTestClassConstructorThrow Verify(errorMessage == result.TestFailureException!.Message); } - public void TestMethodInfoInvokeShouldSetErrorMessageIfTestClassConstructorThrowsWithoutInnerException() + public async Task TestMethodInfoInvokeShouldSetErrorMessageIfTestClassConstructorThrowsWithoutInnerException() { ConstructorInfo ctorInfo = typeof(DummyTestClassWithParameterizedCtor).GetConstructors().Single(); var testClass = new TestClassInfo(typeof(DummyTestClassWithParameterizedCtor), ctorInfo, true, _classAttribute, _testAssemblyInfo); @@ -298,7 +297,7 @@ public void TestMethodInfoInvokeShouldSetErrorMessageIfTestClassConstructorThrow Executor = _testMethodAttribute, }; - UTF.TestResult result = method.Invoke(null); + TestResult result = await method.InvokeAsync(null); string errorMessage = string.Format( CultureInfo.InvariantCulture, Resource.UTA_InstanceCreationError, @@ -309,11 +308,11 @@ public void TestMethodInfoInvokeShouldSetErrorMessageIfTestClassConstructorThrow Verify(errorMessage == result.TestFailureException!.Message); } - public void TestMethodInfoInvokeShouldSetStackTraceInformationIfTestClassConstructorThrows() + public async Task TestMethodInfoInvokeShouldSetStackTraceInformationIfTestClassConstructorThrows() { DummyTestClass.TestConstructorMethodBody = () => throw new NotImplementedException("dummyExceptionMessage"); - var exception = _testMethodInfo.Invoke(null).TestFailureException as TestFailedException; + var exception = (await _testMethodInfo.InvokeAsync(null)).TestFailureException as TestFailedException; Verify(exception is not null); Verify(exception.StackTraceInformation is not null); @@ -322,7 +321,7 @@ public void TestMethodInfoInvokeShouldSetStackTraceInformationIfTestClassConstru " at Microsoft.VisualStudio.TestPlatform.MSTestAdapter.UnitTests.Execution.TestMethodInfoTests.<>c.b__", StringComparison.Ordinal)); } - public void TestMethodInfoInvokeShouldSetStackTraceInformationIfTestClassConstructorThrowsWithoutInnerException() + public async Task TestMethodInfoInvokeShouldSetStackTraceInformationIfTestClassConstructorThrowsWithoutInnerException() { ConstructorInfo ctorInfo = typeof(DummyTestClassWithParameterizedCtor).GetConstructors().Single(); var testClass = new TestClassInfo(typeof(DummyTestClassWithParameterizedCtor), ctorInfo, true, _classAttribute, _testAssemblyInfo); @@ -332,7 +331,7 @@ public void TestMethodInfoInvokeShouldSetStackTraceInformationIfTestClassConstru Executor = _testMethodAttribute, }; - var exception = method.Invoke(null).TestFailureException as TestFailedException; + var exception = (await method.InvokeAsync(null)).TestFailureException as TestFailedException; Verify(exception is not null); Verify(exception.StackTraceInformation is not null); @@ -341,11 +340,11 @@ public void TestMethodInfoInvokeShouldSetStackTraceInformationIfTestClassConstru " at System.Reflection.RuntimeConstructorInfo.Invoke(BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)", StringComparison.Ordinal)); } - public void TestMethodInfoInvokeShouldSetResultFilesIfTestContextHasAttachments() + public async Task TestMethodInfoInvokeShouldSetResultFilesIfTestContextHasAttachments() { Mock testContext = new(); - testContext.Setup(tc => tc.GetResultFiles()).Returns(new List { "C:\\temp.txt" }); - var mockInnerContext = new Mock(); + testContext.Setup(tc => tc.GetResultFiles()).Returns(["C:\\temp.txt"]); + var mockInnerContext = new Mock(); testContext.SetupGet(tc => tc.Context).Returns(mockInnerContext.Object); mockInnerContext.SetupGet(tc => tc.CancellationTokenSource).Returns(new CancellationTokenSource()); @@ -355,13 +354,13 @@ public void TestMethodInfoInvokeShouldSetResultFilesIfTestContextHasAttachments( Executor = _testMethodAttribute, }; - UTF.TestResult result = method.Invoke(null); + TestResult result = await method.InvokeAsync(null); Verify(result.ResultFiles!.Contains("C:\\temp.txt")); } - public void TestMethodInfoInvoke_WhenCtorHasOneParameterOfTypeTestContext_SetsItToTestContext() + public async Task TestMethodInfoInvoke_WhenCtorHasOneParameterOfTypeTestContext_SetsItToTestContext() { - ConstructorInfo ctorInfo = typeof(DummyTestClass).GetConstructor([typeof(UTFExtension.TestContext)])!; + ConstructorInfo ctorInfo = typeof(DummyTestClass).GetConstructor([typeof(TestContext)])!; var testClassInfo = new TestClassInfo(typeof(DummyTestClass), ctorInfo, false, _classAttribute, _testAssemblyInfo); var testMethodInfo = new TestMethodInfo(_methodInfo, testClassInfo, _testContextImplementation) { @@ -369,7 +368,7 @@ public void TestMethodInfoInvoke_WhenCtorHasOneParameterOfTypeTestContext_SetsIt Executor = _testMethodAttribute, }; - UTF.TestResult result = testMethodInfo.Invoke(null); + TestResult result = await testMethodInfo.InvokeAsync(null); Verify(result.Outcome == UTF.UnitTestOutcome.Passed); } @@ -378,7 +377,7 @@ public void TestMethodInfoInvoke_WhenCtorHasOneParameterOfTypeTestContext_SetsIt #region TestClass.TestContext property setup - public void TestMethodInfoInvokeShouldNotThrowIfTestContextIsNotPresent() + public async Task TestMethodInfoInvokeShouldNotThrowIfTestContextIsNotPresent() { var testClass = new TestClassInfo(typeof(DummyTestClass), _constructorInfo, true, _classAttribute, _testAssemblyInfo); var method = new TestMethodInfo(_methodInfo, testClass, _testContextImplementation) @@ -387,14 +386,16 @@ public void TestMethodInfoInvokeShouldNotThrowIfTestContextIsNotPresent() Executor = _testMethodAttribute, }; - UTF.TestResult result; - void RunMethod() => result = method.Invoke(null); +#pragma warning disable IDE0059 // Unnecessary assignment of a value - fp + TestResult result = null!; +#pragma warning restore IDE0059 // Unnecessary assignment of a value + async Task RunMethod() => result = await method.InvokeAsync(null); - RunMethod(); + await RunMethod(); Verify(result.Outcome == UTF.UnitTestOutcome.Passed); } - public void TestMethodInfoInvokeShouldNotThrowIfTestContextDoesNotHaveASetter() + public async Task TestMethodInfoInvokeShouldNotThrowIfTestContextDoesNotHaveASetter() { var testClass = new TestClassInfo(typeof(DummyTestClass), _constructorInfo, true, _classAttribute, _testAssemblyInfo); var method = new TestMethodInfo(_methodInfo, testClass, _testContextImplementation) @@ -403,37 +404,39 @@ public void TestMethodInfoInvokeShouldNotThrowIfTestContextDoesNotHaveASetter() Executor = _testMethodAttribute, }; - UTF.TestResult result; - void RunMethod() => result = method.Invoke(null); +#pragma warning disable IDE0059 // Unnecessary assignment of a value - fp + TestResult result = null!; +#pragma warning restore IDE0059 // Unnecessary assignment of a value + async Task RunMethod() => result = await method.InvokeAsync(null); - RunMethod(); + await RunMethod(); Verify(result.Outcome == UTF.UnitTestOutcome.Passed); } - public void TestMethodInfoInvokeShouldSetTestContextForTestClassInstance() + public async Task TestMethodInfoInvokeShouldSetTestContextForTestClassInstance() { - UTFExtension.TestContext? testContext = null; - DummyTestClass.TestContextSetterBody = context => testContext = context as UTFExtension.TestContext; + TestContext? testContext = null; + DummyTestClass.TestContextSetterBody = context => testContext = context as TestContext; - _testMethodInfo.Invoke(null); + await _testMethodInfo.InvokeAsync(null); Verify(_testContextImplementation.Equals(testContext)); } - public void TestMethodInfoInvokeShouldMarkOutcomeFailedIfSetTestContextThrows() + public async Task TestMethodInfoInvokeShouldMarkOutcomeFailedIfSetTestContextThrows() { DummyTestClass.TestContextSetterBody = value => throw new NotImplementedException(); - UTF.TestResult result = _testMethodInfo.Invoke(null); + TestResult result = await _testMethodInfo.InvokeAsync(null); Verify(result.Outcome == UTF.UnitTestOutcome.Failed); } - public void TestMethodInfoInvokeShouldSetErrorMessageIfSetTestContextThrows() + public async Task TestMethodInfoInvokeShouldSetErrorMessageIfSetTestContextThrows() { DummyTestClass.TestContextSetterBody = value => throw new NotImplementedException("dummyExceptionMessage"); - var exception = _testMethodInfo.Invoke(null).TestFailureException as TestFailedException; + var exception = (await _testMethodInfo.InvokeAsync(null)).TestFailureException as TestFailedException; string errorMessage = string.Format( CultureInfo.InvariantCulture, @@ -444,11 +447,11 @@ public void TestMethodInfoInvokeShouldSetErrorMessageIfSetTestContextThrows() Verify(errorMessage == exception?.Message); } - public void TestMethodInfoInvokeShouldSetStackTraceInformationIfSetTestContextThrows() + public async Task TestMethodInfoInvokeShouldSetStackTraceInformationIfSetTestContextThrows() { DummyTestClass.TestConstructorMethodBody = () => throw new NotImplementedException("dummyExceptionMessage"); - var exception = _testMethodInfo.Invoke(null).TestFailureException as TestFailedException; + var exception = (await _testMethodInfo.InvokeAsync(null)).TestFailureException as TestFailedException; Verify(exception is not null); Verify(exception.StackTraceInformation is not null); @@ -457,19 +460,19 @@ public void TestMethodInfoInvokeShouldSetStackTraceInformationIfSetTestContextTh " at Microsoft.VisualStudio.TestPlatform.MSTestAdapter.UnitTests.Execution.TestMethodInfoTests.<>c.b__", StringComparison.Ordinal)); } - public void TestMethodInfoInvoke_WhenCtorHasOneParameterOfTypeTestContextAndTestContextProperty_InitializeBothTestContexts() + public async Task TestMethodInfoInvoke_WhenCtorHasOneParameterOfTypeTestContextAndTestContextProperty_InitializeBothTestContexts() { - ConstructorInfo ctorInfo = typeof(DummyTestClass).GetConstructor([typeof(UTFExtension.TestContext)])!; + ConstructorInfo ctorInfo = typeof(DummyTestClass).GetConstructor([typeof(TestContext)])!; var testClassInfo = new TestClassInfo(typeof(DummyTestClass), ctorInfo, false, _classAttribute, _testAssemblyInfo); var testMethodInfo = new TestMethodInfo(_methodInfo, testClassInfo, _testContextImplementation) { TimeoutInfo = TimeoutInfo.FromTimeout(3600 * 1000), Executor = _testMethodAttribute, }; - UTFExtension.TestContext? testContext = null; - DummyTestClass.TestContextSetterBody = context => testContext = context as UTFExtension.TestContext; + TestContext? testContext = null; + DummyTestClass.TestContextSetterBody = context => testContext = context as TestContext; - UTF.TestResult result = testMethodInfo.Invoke(null); + TestResult result = await testMethodInfo.InvokeAsync(null); Verify(result.Outcome == UTF.UnitTestOutcome.Passed); Verify(_testContextImplementation.Equals(testContext)); @@ -479,31 +482,31 @@ public void TestMethodInfoInvoke_WhenCtorHasOneParameterOfTypeTestContextAndTest #region TestInitialize method setup - public void TestMethodInfoInvokeShouldCallTestInitialize() + public async Task TestMethodInfoInvokeShouldCallTestInitialize() { bool testInitializeCalled = false; DummyTestClass.TestInitializeMethodBody = classInstance => testInitializeCalled = true; _testClassInfo.TestInitializeMethod = typeof(DummyTestClass).GetMethod("DummyTestInitializeMethod")!; - UTF.TestResult result = _testMethodInfo.Invoke(null); + TestResult result = await _testMethodInfo.InvokeAsync(null); Verify(testInitializeCalled); Verify(result.Outcome == UTF.UnitTestOutcome.Passed); } - public void TestMethodInfoInvokeShouldCallAsyncTestInitializeAndWaitForCompletion() + public async Task TestMethodInfoInvokeShouldCallAsyncTestInitializeAndWaitForCompletion() { bool testInitializeCalled = false; DummyTestClass.DummyAsyncTestMethodBody = () => Task.Run(() => testInitializeCalled = true); _testClassInfo.TestInitializeMethod = typeof(DummyTestClass).GetMethod("DummyAsyncTestMethod")!; - UTF.TestResult result = _testMethodInfo.Invoke(null); + TestResult result = await _testMethodInfo.InvokeAsync(null); Verify(testInitializeCalled); Verify(result.Outcome == UTF.UnitTestOutcome.Passed); } - public void TestMethodInfoInvokeShouldCallTestInitializeOfAllBaseClasses() + public async Task TestMethodInfoInvokeShouldCallTestInitializeOfAllBaseClasses() { var callOrder = new List(); DummyTestClassBase.BaseTestClassMethodBody = classInstance => callOrder.Add("baseTestInitializeCalled2"); @@ -513,7 +516,7 @@ public void TestMethodInfoInvokeShouldCallTestInitializeOfAllBaseClasses() _testClassInfo.BaseTestInitializeMethodsQueue.Enqueue(typeof(DummyTestClassBase).GetMethod("DummyBaseTestClassMethod")!); _testClassInfo.BaseTestInitializeMethodsQueue.Enqueue(typeof(DummyTestClass).GetMethod("DummyAsyncTestMethod")!); - UTF.TestResult result = _testMethodInfo.Invoke(null); + TestResult result = await _testMethodInfo.InvokeAsync(null); var expectedCallOrder = new List { @@ -525,25 +528,25 @@ public void TestMethodInfoInvokeShouldCallTestInitializeOfAllBaseClasses() Verify(expectedCallOrder.SequenceEqual(callOrder)); } - public void TestMethodInfoInvokeShouldNotThrowIfTestInitializeIsNull() + public async Task TestMethodInfoInvokeShouldNotThrowIfTestInitializeIsNull() { _testClassInfo.TestInitializeMethod = null; - UTF.TestResult result = _testMethodInfo.Invoke(null); + TestResult result = await _testMethodInfo.InvokeAsync(null); Verify(result.Outcome == UTF.UnitTestOutcome.Passed); } - public void TestMethodInfoInvokeShouldNotThrowIfTestInitializeForBaseClassIsNull() + public async Task TestMethodInfoInvokeShouldNotThrowIfTestInitializeForBaseClassIsNull() { _testClassInfo.BaseTestInitializeMethodsQueue.Enqueue(null!); - UTF.TestResult result = _testMethodInfo.Invoke(null); + TestResult result = await _testMethodInfo.InvokeAsync(null); Verify(result.Outcome == UTF.UnitTestOutcome.Passed); } - public void TestMethodInfoInvokeWhenTestThrowsReturnsExpectedResult() + public async Task TestMethodInfoInvokeWhenTestThrowsReturnsExpectedResult() { // Arrange. DummyTestClass.TestInitializeMethodBody = classInstance => throw new ArgumentException("Some exception message", new InvalidOperationException("Inner exception message")); @@ -563,7 +566,7 @@ public void TestMethodInfoInvokeWhenTestThrowsReturnsExpectedResult() }; // Act. - UTF.TestResult result = testMethodInfo.Invoke(null); + TestResult result = await testMethodInfo.InvokeAsync(null); // Assert. Verify(result.Outcome == UTF.UnitTestOutcome.Failed); @@ -580,7 +583,7 @@ public void TestMethodInfoInvokeWhenTestThrowsReturnsExpectedResult() #endif } - public void TestInitialize_WhenTestReturnsTaskFromException_DisplayProperException() + public async Task TestInitialize_WhenTestReturnsTaskFromException_DisplayProperException() { // Arrange. DummyTestClass.TestInitializeMethodBodyAsync = async classInstance => await Task.FromException(new Exception("Outer", new InvalidOperationException("Inner"))); @@ -593,7 +596,7 @@ public void TestInitialize_WhenTestReturnsTaskFromException_DisplayProperExcepti }; // Act. - UTF.TestResult result = testMethodInfo.Invoke(null); + TestResult result = await testMethodInfo.InvokeAsync(null); // Assert. Verify(result.Outcome == UTF.UnitTestOutcome.Failed); @@ -614,7 +617,7 @@ public void TestInitialize_WhenTestReturnsTaskFromException_DisplayProperExcepti Verify(exception.StackTraceInformation is not null); } - public void TestMethodInfoInvokeWhenTestThrowsAssertFailReturnsExpectedResult() + public async Task TestMethodInfoInvokeWhenTestThrowsAssertFailReturnsExpectedResult() { // Arrange. DummyTestClass.TestInitializeMethodBody = classInstance => UTF.Assert.Fail("dummyFailMessage"); @@ -634,7 +637,7 @@ public void TestMethodInfoInvokeWhenTestThrowsAssertFailReturnsExpectedResult() }; // Act. - UTF.TestResult result = testMethodInfo.Invoke(null); + TestResult result = await testMethodInfo.InvokeAsync(null); // Assert. Verify(result.Outcome == UTF.UnitTestOutcome.Failed); @@ -643,14 +646,14 @@ public void TestMethodInfoInvokeWhenTestThrowsAssertFailReturnsExpectedResult() Verify(exception is not null); Verify(errorMessage == exception.Message); Verify(exception.Outcome == UTF.UnitTestOutcome.Failed); - Verify(exception.InnerException!.GetType() == typeof(UTF.AssertFailedException)); + Verify(exception.InnerException!.GetType() == typeof(AssertFailedException)); #if DEBUG Verify(exception.StackTraceInformation!.ErrorStackTrace.StartsWith( " at Microsoft.VisualStudio.TestPlatform.MSTestAdapter.UnitTests.Execution.TestMethodInfoTests.<>c.b__", StringComparison.Ordinal)); #endif } - public void TestMethodInfoInvokeWhenTestThrowsAssertInconclusiveReturnsExpectedResult() + public async Task TestMethodInfoInvokeWhenTestThrowsAssertInconclusiveReturnsExpectedResult() { // Arrange. DummyTestClass.TestInitializeMethodBody = classInstance => UTF.Assert.Inconclusive("dummyFailMessage"); @@ -670,7 +673,7 @@ public void TestMethodInfoInvokeWhenTestThrowsAssertInconclusiveReturnsExpectedR }; // Act. - UTF.TestResult result = testMethodInfo.Invoke(null); + TestResult result = await testMethodInfo.InvokeAsync(null); // Assert. Verify(result.Outcome == UTF.UnitTestOutcome.Inconclusive); @@ -679,7 +682,7 @@ public void TestMethodInfoInvokeWhenTestThrowsAssertInconclusiveReturnsExpectedR Verify(exception is not null); Verify(errorMessage == exception.Message); Verify(exception.Outcome == UTF.UnitTestOutcome.Inconclusive); - Verify(exception.InnerException!.GetType() == typeof(UTF.AssertInconclusiveException)); + Verify(exception.InnerException!.GetType() == typeof(AssertInconclusiveException)); #if DEBUG Verify(exception.StackTraceInformation!.ErrorStackTrace.StartsWith( " at Microsoft.VisualStudio.TestPlatform.MSTestAdapter.UnitTests.Execution.TestMethodInfoTests.<>c.b__", StringComparison.Ordinal)); @@ -690,7 +693,7 @@ public void TestMethodInfoInvokeWhenTestThrowsAssertInconclusiveReturnsExpectedR #region TestCleanup method setup - public void TestCleanup_WhenTestReturnsTaskFromException_DisplayProperException() + public async Task TestCleanup_WhenTestReturnsTaskFromException_DisplayProperException() { // Arrange. DummyTestClass.TestCleanupMethodBodyAsync = async classInstance => await Task.FromException(new Exception("Outer", new InvalidOperationException("Inner"))); @@ -702,7 +705,7 @@ public void TestCleanup_WhenTestReturnsTaskFromException_DisplayProperException( }; // Act. - UTF.TestResult result = testMethodInfo.Invoke(null); + TestResult result = await testMethodInfo.InvokeAsync(null); // Assert. Verify(result.Outcome == UTF.UnitTestOutcome.Failed); @@ -727,40 +730,40 @@ public void TestCleanup_WhenTestReturnsTaskFromException_DisplayProperException( } } - public void TestMethodInfoInvokeShouldCallTestCleanup() + public async Task TestMethodInfoInvokeShouldCallTestCleanup() { bool cleanupMethodCalled = false; DummyTestClass.DummyAsyncTestMethodBody = () => Task.Run(() => cleanupMethodCalled = true); _testClassInfo.TestCleanupMethod = typeof(DummyTestClass).GetMethod("DummyAsyncTestMethod")!; - UTF.TestResult result = _testMethodInfo.Invoke(null); + TestResult result = await _testMethodInfo.InvokeAsync(null); Verify(result.Outcome == UTF.UnitTestOutcome.Passed); Verify(cleanupMethodCalled); } - public void TestMethodInfoInvokeShouldCallAsyncTestCleanup() + public async Task TestMethodInfoInvokeShouldCallAsyncTestCleanup() { bool cleanupMethodCalled = false; DummyTestClass.TestCleanupMethodBody = classInstance => cleanupMethodCalled = true; _testClassInfo.TestCleanupMethod = typeof(DummyTestClass).GetMethod("DummyTestCleanupMethod")!; - UTF.TestResult result = _testMethodInfo.Invoke(null); + TestResult result = await _testMethodInfo.InvokeAsync(null); Verify(result.Outcome == UTF.UnitTestOutcome.Passed); Verify(cleanupMethodCalled); } - public void TestMethodInfoInvokeShouldNotThrowIfTestCleanupMethodIsNull() + public async Task TestMethodInfoInvokeShouldNotThrowIfTestCleanupMethodIsNull() { _testClassInfo.TestCleanupMethod = null; - UTF.TestResult result = _testMethodInfo.Invoke(null); + TestResult result = await _testMethodInfo.InvokeAsync(null); Verify(result.Outcome == UTF.UnitTestOutcome.Passed); } - public void TestMethodInfoInvokeShouldCallTestCleanupForBaseTestClasses() + public async Task TestMethodInfoInvokeShouldCallTestCleanupForBaseTestClasses() { var callOrder = new List(); DummyTestClassBase.BaseTestClassMethodBody = classInstance => callOrder.Add("baseTestCleanupCalled" + callOrder.Count); @@ -769,7 +772,7 @@ public void TestMethodInfoInvokeShouldCallTestCleanupForBaseTestClasses() _testClassInfo.BaseTestCleanupMethodsQueue.Enqueue(typeof(DummyTestClassBase).GetMethod("DummyBaseTestClassMethod")!); _testClassInfo.BaseTestCleanupMethodsQueue.Enqueue(typeof(DummyTestClassBase).GetMethod("DummyBaseTestClassMethod")!); - UTF.TestResult result = _testMethodInfo.Invoke(null); + TestResult result = await _testMethodInfo.InvokeAsync(null); var expectedCallOrder = new List { @@ -781,7 +784,7 @@ public void TestMethodInfoInvokeShouldCallTestCleanupForBaseTestClasses() Verify(expectedCallOrder.SequenceEqual(callOrder)); } - public void TestMethodInfoInvokeShouldCallTestCleanupForBaseTestClassesAlways() + public async Task TestMethodInfoInvokeShouldCallTestCleanupForBaseTestClassesAlways() { var callOrder = new List(); DummyTestClassBase.BaseTestClassMethodBody = classInstance => callOrder.Add("baseTestCleanupCalled" + callOrder.Count); @@ -790,8 +793,8 @@ public void TestMethodInfoInvokeShouldCallTestCleanupForBaseTestClassesAlways() _testClassInfo.BaseTestCleanupMethodsQueue.Enqueue(typeof(DummyTestClassBase).GetMethod("DummyBaseTestClassMethod")!); _testClassInfo.BaseTestCleanupMethodsQueue.Enqueue(typeof(DummyTestClassBase).GetMethod("DummyBaseTestClassMethod")!); - _testMethodInfo.Invoke(null); - UTF.TestResult result = _testMethodInfo.Invoke(null); + await _testMethodInfo.InvokeAsync(null); + TestResult result = await _testMethodInfo.InvokeAsync(null); var expectedCallOrder = new List { @@ -807,7 +810,7 @@ public void TestMethodInfoInvokeShouldCallTestCleanupForBaseTestClassesAlways() Verify(expectedCallOrder.SequenceEqual(callOrder)); } - public void TestMethodInfoInvokeWhenTestCleanupThrowsReturnsExpectedResult() + public async Task TestMethodInfoInvokeWhenTestCleanupThrowsReturnsExpectedResult() { DummyTestClass.TestCleanupMethodBody = classInstance => throw new ArgumentException("Some exception message", new InvalidOperationException("Inner exception message")); _testClassInfo.TestCleanupMethod = typeof(DummyTestClass).GetMethod("DummyTestCleanupMethod")!; @@ -819,7 +822,7 @@ public void TestMethodInfoInvokeWhenTestCleanupThrowsReturnsExpectedResult() _testClassInfo.TestCleanupMethod!.Name, "System.ArgumentException: Some exception message ---> System.InvalidOperationException: Inner exception message"); - UTF.TestResult result = _testMethodInfo.Invoke(null); + TestResult result = await _testMethodInfo.InvokeAsync(null); Verify(result.Outcome == UTF.UnitTestOutcome.Failed); @@ -836,7 +839,7 @@ public void TestMethodInfoInvokeWhenTestCleanupThrowsReturnsExpectedResult() #endif } - public void TestMethodInfoInvokeWhenTestCleanupThrowsAssertInconclusiveReturnsExpectedResult() + public async Task TestMethodInfoInvokeWhenTestCleanupThrowsAssertInconclusiveReturnsExpectedResult() { DummyTestClass.TestCleanupMethodBody = classInstance => UTF.Assert.Inconclusive("Test inconclusive"); _testClassInfo.TestCleanupMethod = typeof(DummyTestClass).GetMethod("DummyTestCleanupMethod")!; @@ -848,7 +851,7 @@ public void TestMethodInfoInvokeWhenTestCleanupThrowsAssertInconclusiveReturnsEx _testClassInfo.TestCleanupMethod.Name, "Microsoft.VisualStudio.TestTools.UnitTesting.AssertInconclusiveException: Assert.Inconclusive failed. Test inconclusive"); - UTF.TestResult result = _testMethodInfo.Invoke(null); + TestResult result = await _testMethodInfo.InvokeAsync(null); Verify(result.Outcome == UTF.UnitTestOutcome.Inconclusive); @@ -856,14 +859,14 @@ public void TestMethodInfoInvokeWhenTestCleanupThrowsAssertInconclusiveReturnsEx Verify(exception is not null); Verify(exception.Outcome == UTF.UnitTestOutcome.Inconclusive); Verify(expectedErrorMessage == exception.Message); - Verify(exception.InnerException!.GetType() == typeof(UTF.AssertInconclusiveException)); + Verify(exception.InnerException!.GetType() == typeof(AssertInconclusiveException)); #if DEBUG Verify(exception.StackTraceInformation!.ErrorStackTrace.StartsWith( " at Microsoft.VisualStudio.TestPlatform.MSTestAdapter.UnitTests.Execution.TestMethodInfoTests.<>c.b__", StringComparison.Ordinal)); #endif } - public void TestMethodInfoInvokeWhenTestCleanupThrowsAssertFailedReturnsExpectedResult() + public async Task TestMethodInfoInvokeWhenTestCleanupThrowsAssertFailedReturnsExpectedResult() { DummyTestClass.TestCleanupMethodBody = classInstance => UTF.Assert.Fail("Test failed"); _testClassInfo.TestCleanupMethod = typeof(DummyTestClass).GetMethod("DummyTestCleanupMethod")!; @@ -875,7 +878,7 @@ public void TestMethodInfoInvokeWhenTestCleanupThrowsAssertFailedReturnsExpected _testClassInfo.TestCleanupMethod!.Name, "Microsoft.VisualStudio.TestTools.UnitTesting.AssertFailedException: Assert.Fail failed. Test failed"); - UTF.TestResult result = _testMethodInfo.Invoke(null); + TestResult result = await _testMethodInfo.InvokeAsync(null); Verify(result.Outcome == UTF.UnitTestOutcome.Failed); @@ -883,20 +886,20 @@ public void TestMethodInfoInvokeWhenTestCleanupThrowsAssertFailedReturnsExpected Verify(exception is not null); Verify(exception.Outcome == UTF.UnitTestOutcome.Failed); Verify(expectedErrorMessage == exception.Message); - Verify(exception.InnerException!.GetType() == typeof(UTF.AssertFailedException)); + Verify(exception.InnerException!.GetType() == typeof(AssertFailedException)); #if DEBUG Verify(exception.StackTraceInformation!.ErrorStackTrace.StartsWith( " at Microsoft.VisualStudio.TestPlatform.MSTestAdapter.UnitTests.Execution.TestMethodInfoTests.<>c.b__", StringComparison.Ordinal)); #endif } - public void TestMethodInfoInvokeShouldAppendErrorMessagesIfBothTestMethodAndTestCleanupThrows() + public async Task TestMethodInfoInvokeShouldAppendErrorMessagesIfBothTestMethodAndTestCleanupThrows() { DummyTestClass.TestCleanupMethodBody = classInstance => throw new NotImplementedException("dummyErrorMessage"); DummyTestClass.TestMethodBody = classInstance => throw new NotImplementedException("dummyMethodError"); _testClassInfo.TestCleanupMethod = typeof(DummyTestClass).GetMethod("DummyTestCleanupMethod")!; - UTF.TestResult result = _testMethodInfo.Invoke(null); + TestResult result = await _testMethodInfo.InvokeAsync(null); var exception = result.TestFailureException as AggregateException; string errorMessage = string.Format( CultureInfo.CurrentCulture, @@ -917,13 +920,13 @@ public void TestMethodInfoInvokeShouldAppendErrorMessagesIfBothTestMethodAndTest Verify(exception.InnerExceptions[1].Message.Contains(cleanupError)); } - public void TestMethodInfoInvokeShouldAppendStackTraceInformationIfBothTestMethodAndTestCleanupThrows() + public async Task TestMethodInfoInvokeShouldAppendStackTraceInformationIfBothTestMethodAndTestCleanupThrows() { DummyTestClass.TestCleanupMethodBody = classInstance => throw new NotImplementedException(); DummyTestClass.TestMethodBody = classInstance => throw new NotImplementedException("dummyMethodError"); _testClassInfo.TestCleanupMethod = typeof(DummyTestClass).GetMethod("DummyTestCleanupMethod")!; - UTF.TestResult result = _testMethodInfo.Invoke(null); + TestResult result = await _testMethodInfo.InvokeAsync(null); var exception = result.TestFailureException as AggregateException; Verify(result.Outcome == UTF.UnitTestOutcome.Failed); @@ -934,12 +937,12 @@ public void TestMethodInfoInvokeShouldAppendStackTraceInformationIfBothTestMetho #endif } - public void TestMethodInfoInvokeShouldSetOutcomeAsInconclusiveIfTestCleanupIsInconclusive() + public async Task TestMethodInfoInvokeShouldSetOutcomeAsInconclusiveIfTestCleanupIsInconclusive() { - DummyTestClass.TestCleanupMethodBody = classInstance => throw new UTF.AssertInconclusiveException(); + DummyTestClass.TestCleanupMethodBody = classInstance => throw new AssertInconclusiveException(); _testClassInfo.TestCleanupMethod = typeof(DummyTestClass).GetMethod("DummyTestCleanupMethod")!; - UTF.TestResult result = _testMethodInfo.Invoke(null); + TestResult result = await _testMethodInfo.InvokeAsync(null); var exception = result.TestFailureException as TestFailedException; Verify(result.Outcome == UTF.UnitTestOutcome.Inconclusive); @@ -947,18 +950,18 @@ public void TestMethodInfoInvokeShouldSetOutcomeAsInconclusiveIfTestCleanupIsInc Verify(exception.Message.Contains("Microsoft.VisualStudio.TestTools.UnitTesting.AssertInconclusiveException")); } - public void TestMethodInfoInvokeShouldSetMoreImportantOutcomeIfTestCleanupIsInconclusiveButTestMethodFails() + public async Task TestMethodInfoInvokeShouldSetMoreImportantOutcomeIfTestCleanupIsInconclusiveButTestMethodFails() { - DummyTestClass.TestCleanupMethodBody = classInstance => throw new UTF.AssertInconclusiveException(); + DummyTestClass.TestCleanupMethodBody = classInstance => throw new AssertInconclusiveException(); DummyTestClass.TestMethodBody = classInstance => Fail(); _testClassInfo.TestCleanupMethod = typeof(DummyTestClass).GetMethod("DummyTestCleanupMethod")!; - UTF.TestResult result = _testMethodInfo.Invoke(null); + TestResult result = await _testMethodInfo.InvokeAsync(null); Verify(result.Outcome == UTF.UnitTestOutcome.Failed); } - public void TestMethodInfoInvokeShouldCallDisposeForDisposableTestClass() + public async Task TestMethodInfoInvokeShouldCallDisposeForDisposableTestClass() { bool disposeCalled = false; DummyTestClassWithDisposable.DisposeMethodBody = () => disposeCalled = true; @@ -970,13 +973,13 @@ public void TestMethodInfoInvokeShouldCallDisposeForDisposableTestClass() Executor = _testMethodAttribute, }; - method.Invoke(null); + await method.InvokeAsync(null); Verify(disposeCalled); } #if NET6_0_OR_GREATER - public void TestMethodInfoInvoke_WhenTestClassIsAsyncDisposable_ShouldDisposeAsync() + public async Task TestMethodInfoInvoke_WhenTestClassIsAsyncDisposable_ShouldDisposeAsync() { // Arrange bool asyncDisposeCalled = false; @@ -990,13 +993,13 @@ public void TestMethodInfoInvoke_WhenTestClassIsAsyncDisposable_ShouldDisposeAsy }; // Act - method.Invoke(null); + await method.InvokeAsync(null); // Assert Verify(asyncDisposeCalled); } - public void TestMethodInfoInvoke_WhenTestClassIsDisposableAndAsyncDisposable_ShouldCallAsyncDisposeThenDispose() + public async Task TestMethodInfoInvoke_WhenTestClassIsDisposableAndAsyncDisposable_ShouldCallAsyncDisposeThenDispose() { // Arrange int order = 0; @@ -1015,7 +1018,7 @@ public void TestMethodInfoInvoke_WhenTestClassIsDisposableAndAsyncDisposable_Sho }; // Act - method.Invoke(null); + await method.InvokeAsync(null); // Assert Verify(disposeCalledOrder == 2); @@ -1023,7 +1026,7 @@ public void TestMethodInfoInvoke_WhenTestClassIsDisposableAndAsyncDisposable_Sho } #endif - public void TestMethodInfoInvokeShouldCallDisposeForDisposableTestClassIfTestCleanupThrows() + public async Task TestMethodInfoInvokeShouldCallDisposeForDisposableTestClassIfTestCleanupThrows() { bool disposeCalled = false; DummyTestClassWithDisposable.DisposeMethodBody = () => disposeCalled = true; @@ -1039,25 +1042,25 @@ public void TestMethodInfoInvokeShouldCallDisposeForDisposableTestClassIfTestCle Executor = _testMethodAttribute, }; - method.Invoke(null); + await method.InvokeAsync(null); Verify(disposeCalled); } - public void TestMethodInfoInvokeShouldCallTestCleanupEvenIfTestMethodThrows() + public async Task TestMethodInfoInvokeShouldCallTestCleanupEvenIfTestMethodThrows() { bool testCleanupMethodCalled = false; DummyTestClass.TestMethodBody = classInstance => throw new NotImplementedException(); DummyTestClass.TestCleanupMethodBody = classInstance => testCleanupMethodCalled = true; _testClassInfo.TestCleanupMethod = typeof(DummyTestClass).GetMethod("DummyTestCleanupMethod")!; - UTF.TestResult result = _testMethodInfo.Invoke(null); + TestResult result = await _testMethodInfo.InvokeAsync(null); Verify(testCleanupMethodCalled); Verify(result.Outcome == UTF.UnitTestOutcome.Failed); } - public void TestMethodInfoInvokeShouldCallTestCleanupEvenIfTestInitializeMethodThrows() + public async Task TestMethodInfoInvokeShouldCallTestCleanupEvenIfTestInitializeMethodThrows() { bool testCleanupMethodCalled = false; DummyTestClass.TestInitializeMethodBody = classInstance => throw new NotImplementedException(); @@ -1065,13 +1068,13 @@ public void TestMethodInfoInvokeShouldCallTestCleanupEvenIfTestInitializeMethodT _testClassInfo.TestInitializeMethod = typeof(DummyTestClass).GetMethod("DummyTestInitializeMethod")!; _testClassInfo.TestCleanupMethod = typeof(DummyTestClass).GetMethod("DummyTestCleanupMethod")!; - UTF.TestResult result = _testMethodInfo.Invoke(null); + TestResult result = await _testMethodInfo.InvokeAsync(null); Verify(testCleanupMethodCalled); Verify(result.Outcome == UTF.UnitTestOutcome.Failed); } - public void TestMethodInfoInvokeShouldCallTestCleanupIfTestClassInstanceIsNotNull() + public async Task TestMethodInfoInvokeShouldCallTestCleanupIfTestClassInstanceIsNotNull() { bool testCleanupMethodCalled = false; @@ -1079,25 +1082,25 @@ public void TestMethodInfoInvokeShouldCallTestCleanupIfTestClassInstanceIsNotNul DummyTestClass.TestConstructorMethodBody = () => throw new NotImplementedException(); DummyTestClass.TestCleanupMethodBody = classInstance => testCleanupMethodCalled = true; - UTF.TestResult result = _testMethodInfo.Invoke(null); + TestResult result = await _testMethodInfo.InvokeAsync(null); Verify(!testCleanupMethodCalled); Verify(result.Outcome == UTF.UnitTestOutcome.Failed); } - public void TestMethodInfoInvokeShouldNotCallTestCleanupIfClassSetContextThrows() + public async Task TestMethodInfoInvokeShouldNotCallTestCleanupIfClassSetContextThrows() { bool testCleanupCalled = false; DummyTestClass.TestCleanupMethodBody = classInstance => testCleanupCalled = true; DummyTestClass.TestContextSetterBody = o => throw new NotImplementedException(); _testClassInfo.TestCleanupMethod = typeof(DummyTestClass).GetMethod("DummyTestCleanupMethod")!; - _testMethodInfo.Invoke(null); + await _testMethodInfo.InvokeAsync(null); Verify(!testCleanupCalled); } - public void TestMethodInfoInvokeShouldSetResultAsPassedIfExpectedExceptionIsThrown() + public async Task TestMethodInfoInvokeShouldSetResultAsPassedIfExpectedExceptionIsThrown() { DummyTestClass.TestMethodBody = o => throw new DivideByZeroException(); var testMethodInfo = new TestMethodInfo(_methodInfo, _testClassInfo, _testContextImplementation) @@ -1107,12 +1110,12 @@ public void TestMethodInfoInvokeShouldSetResultAsPassedIfExpectedExceptionIsThro ExpectedException = _expectedException, }; - UTF.TestResult result = testMethodInfo.Invoke(null); + TestResult result = await testMethodInfo.InvokeAsync(null); Verify(result.Outcome == UTF.UnitTestOutcome.Passed); } - public void TestMethodInfoInvokeShouldSetResultAsFailedIfExceptionDifferentFromExpectedExceptionIsThrown() + public async Task TestMethodInfoInvokeShouldSetResultAsFailedIfExceptionDifferentFromExpectedExceptionIsThrown() { DummyTestClass.TestMethodBody = o => throw new IndexOutOfRangeException(); var testMethodInfo = new TestMethodInfo(_methodInfo, _testClassInfo, _testContextImplementation) @@ -1122,7 +1125,7 @@ public void TestMethodInfoInvokeShouldSetResultAsFailedIfExceptionDifferentFromE ExpectedException = _expectedException, }; - UTF.TestResult result = testMethodInfo.Invoke(null); + TestResult result = await testMethodInfo.InvokeAsync(null); Verify(result.Outcome == UTF.UnitTestOutcome.Failed); string message = "Test method threw exception System.IndexOutOfRangeException, but exception System.DivideByZeroException was expected. " + @@ -1130,7 +1133,7 @@ public void TestMethodInfoInvokeShouldSetResultAsFailedIfExceptionDifferentFromE Verify(message == result.TestFailureException!.Message); } - public void TestMethodInfoInvokeShouldSetResultAsFailedWhenExceptionIsExpectedButIsNotThrown() + public async Task TestMethodInfoInvokeShouldSetResultAsFailedWhenExceptionIsExpectedButIsNotThrown() { DummyTestClass.TestMethodBody = o => { }; var testMethodInfo = new TestMethodInfo(_methodInfo, _testClassInfo, _testContextImplementation) @@ -1139,31 +1142,31 @@ public void TestMethodInfoInvokeShouldSetResultAsFailedWhenExceptionIsExpectedBu Executor = _testMethodAttribute, ExpectedException = _expectedException, }; - UTF.TestResult result = testMethodInfo.Invoke(null); + TestResult result = await testMethodInfo.InvokeAsync(null); Verify(result.Outcome == UTF.UnitTestOutcome.Failed); string message = "Test method did not throw expected exception System.DivideByZeroException."; Verify(result.TestFailureException!.Message.Contains(message)); } - public void TestMethodInfoInvokeShouldSetResultAsInconclusiveWhenExceptionIsAssertInconclusiveException() + public async Task TestMethodInfoInvokeShouldSetResultAsInconclusiveWhenExceptionIsAssertInconclusiveException() { - DummyTestClass.TestMethodBody = o => throw new UTF.AssertInconclusiveException(); + DummyTestClass.TestMethodBody = o => throw new AssertInconclusiveException(); var testMethodInfo = new TestMethodInfo(_methodInfo, _testClassInfo, _testContextImplementation) { TimeoutInfo = TimeoutInfo.FromTimeout(3600 * 1000), Executor = _testMethodAttribute, ExpectedException = _expectedException, }; - UTF.TestResult result = testMethodInfo.Invoke(null); + TestResult result = await testMethodInfo.InvokeAsync(null); Verify(result.Outcome == UTF.UnitTestOutcome.Inconclusive); string message = "Exception of type 'Microsoft.VisualStudio.TestTools.UnitTesting.AssertInconclusiveException' was thrown."; Verify(message == result.TestFailureException!.Message); } - public void TestMethodInfoInvokeShouldSetTestOutcomeBeforeTestCleanup() + public async Task TestMethodInfoInvokeShouldSetTestOutcomeBeforeTestCleanup() { UTF.UnitTestOutcome testOutcome = UTF.UnitTestOutcome.Unknown; - DummyTestClass.TestMethodBody = o => throw new UTF.AssertInconclusiveException(); + DummyTestClass.TestMethodBody = o => throw new AssertInconclusiveException(); DummyTestClass.TestCleanupMethodBody = c => { if (DummyTestClass.GetTestContext() != null) @@ -1179,12 +1182,12 @@ public void TestMethodInfoInvokeShouldSetTestOutcomeBeforeTestCleanup() ExpectedException = _expectedException, }; - UTF.TestResult result = testMethodInfo.Invoke(null); + TestResult result = await testMethodInfo.InvokeAsync(null); Verify(testOutcome == UTF.UnitTestOutcome.Inconclusive); } - public void HandleMethodExceptionShouldInvokeVerifyOfCustomExpectedException() + public async Task HandleMethodExceptionShouldInvokeVerifyOfCustomExpectedException() { CustomExpectedExceptionAttribute customExpectedException = new(typeof(DivideByZeroException), "Attempted to divide by zero"); var method = new TestMethodInfo( @@ -1198,12 +1201,12 @@ public void HandleMethodExceptionShouldInvokeVerifyOfCustomExpectedException() }; DummyTestClass.TestMethodBody = o => throw new DivideByZeroException(); - UTF.TestResult result = method.Invoke(null); + TestResult result = await method.InvokeAsync(null); Verify(customExpectedException.IsVerifyInvoked); Verify(result.Outcome == UTF.UnitTestOutcome.Passed); } - public void HandleMethodExceptionShouldSetOutcomeAsFailedIfVerifyOfExpectedExceptionThrows() + public async Task HandleMethodExceptionShouldSetOutcomeAsFailedIfVerifyOfExpectedExceptionThrows() { CustomExpectedExceptionAttribute customExpectedException = new(typeof(DivideByZeroException), "Custom Exception"); var method = new TestMethodInfo( @@ -1217,12 +1220,12 @@ public void HandleMethodExceptionShouldSetOutcomeAsFailedIfVerifyOfExpectedExcep }; DummyTestClass.TestMethodBody = o => throw new DivideByZeroException(); - UTF.TestResult result = method.Invoke(null); + TestResult result = await method.InvokeAsync(null); Verify(result.TestFailureException!.Message == "The exception message doesn't contain the string defined in the exception attribute"); Verify(result.Outcome == UTF.UnitTestOutcome.Failed); } - public void HandleMethodExceptionShouldSetOutcomeAsInconclusiveIfVerifyOfExpectedExceptionThrowsAssertInconclusiveException() + public async Task HandleMethodExceptionShouldSetOutcomeAsInconclusiveIfVerifyOfExpectedExceptionThrowsAssertInconclusiveException() { CustomExpectedExceptionAttribute customExpectedException = new(typeof(DivideByZeroException), "Custom Exception"); var method = new TestMethodInfo( @@ -1235,14 +1238,14 @@ public void HandleMethodExceptionShouldSetOutcomeAsInconclusiveIfVerifyOfExpecte ExpectedException = customExpectedException, }; - DummyTestClass.TestMethodBody = o => throw new UTF.AssertInconclusiveException(); - UTF.TestResult result = method.Invoke(null); + DummyTestClass.TestMethodBody = o => throw new AssertInconclusiveException(); + TestResult result = await method.InvokeAsync(null); string message = "Exception of type 'Microsoft.VisualStudio.TestTools.UnitTesting.AssertInconclusiveException' was thrown."; Verify(result.TestFailureException!.Message == message); Verify(result.Outcome == UTF.UnitTestOutcome.Inconclusive); } - public void HandleMethodExceptionShouldInvokeVerifyOfDerivedCustomExpectedException() + public async Task HandleMethodExceptionShouldInvokeVerifyOfDerivedCustomExpectedException() { DerivedCustomExpectedExceptionAttribute derivedCustomExpectedException = new(typeof(DivideByZeroException), "Attempted to divide by zero"); var method = new TestMethodInfo( @@ -1256,14 +1259,14 @@ public void HandleMethodExceptionShouldInvokeVerifyOfDerivedCustomExpectedExcept }; DummyTestClass.TestMethodBody = o => throw new DivideByZeroException(); - UTF.TestResult result = method.Invoke(null); + TestResult result = await method.InvokeAsync(null); Verify(derivedCustomExpectedException.IsVerifyInvoked); Verify(result.Outcome == UTF.UnitTestOutcome.Passed); } - public void VerifyShouldNotThrowIfThrownExceptionCanBeAssignedToExpectedException() + public async Task VerifyShouldNotThrowIfThrownExceptionCanBeAssignedToExpectedException() { - UTF.ExpectedExceptionAttribute expectedException = new(typeof(Exception)) + ExpectedExceptionAttribute expectedException = new(typeof(Exception)) { AllowDerivedTypes = true, }; @@ -1278,13 +1281,13 @@ public void VerifyShouldNotThrowIfThrownExceptionCanBeAssignedToExpectedExceptio }; DummyTestClass.TestMethodBody = o => throw new DivideByZeroException(); - UTF.TestResult result = method.Invoke(null); + TestResult result = await method.InvokeAsync(null); Verify(result.Outcome == UTF.UnitTestOutcome.Passed); } - public void VerifyShouldThrowExceptionIfThrownExceptionCannotBeAssignedToExpectedException() + public async Task VerifyShouldThrowExceptionIfThrownExceptionCannotBeAssignedToExpectedException() { - UTF.ExpectedExceptionAttribute expectedException = new(typeof(DivideByZeroException), "Custom Exception") + ExpectedExceptionAttribute expectedException = new(typeof(DivideByZeroException), "Custom Exception") { AllowDerivedTypes = true, }; @@ -1299,16 +1302,16 @@ public void VerifyShouldThrowExceptionIfThrownExceptionCannotBeAssignedToExpecte }; DummyTestClass.TestMethodBody = o => throw new ArgumentNullException(); - UTF.TestResult result = method.Invoke(null); + TestResult result = await method.InvokeAsync(null); string message = "Test method threw exception System.ArgumentNullException, but exception System.DivideByZeroException" + " or a type derived from it was expected. Exception message: System.ArgumentNullException: Value cannot be null."; Verify(result.TestFailureException!.Message == message); Verify(result.Outcome == UTF.UnitTestOutcome.Failed); } - public void VerifyShouldRethrowExceptionIfThrownExceptionIsAssertFailedException() + public async Task VerifyShouldRethrowExceptionIfThrownExceptionIsAssertFailedException() { - UTF.ExpectedExceptionAttribute expectedException = new(typeof(DivideByZeroException)) + ExpectedExceptionAttribute expectedException = new(typeof(DivideByZeroException)) { AllowDerivedTypes = true, }; @@ -1322,16 +1325,16 @@ public void VerifyShouldRethrowExceptionIfThrownExceptionIsAssertFailedException ExpectedException = expectedException, }; - DummyTestClass.TestMethodBody = o => throw new UTF.AssertFailedException(); - UTF.TestResult result = method.Invoke(null); + DummyTestClass.TestMethodBody = o => throw new AssertFailedException(); + TestResult result = await method.InvokeAsync(null); string message = "Exception of type 'Microsoft.VisualStudio.TestTools.UnitTesting.AssertFailedException' was thrown."; Verify(result.TestFailureException!.Message == message); Verify(result.Outcome == UTF.UnitTestOutcome.Failed); } - public void VerifyShouldRethrowExceptionIfThrownExceptionIsAssertInconclusiveException() + public async Task VerifyShouldRethrowExceptionIfThrownExceptionIsAssertInconclusiveException() { - UTF.ExpectedExceptionAttribute expectedException = new(typeof(DivideByZeroException)) + ExpectedExceptionAttribute expectedException = new(typeof(DivideByZeroException)) { AllowDerivedTypes = true, }; @@ -1345,16 +1348,16 @@ public void VerifyShouldRethrowExceptionIfThrownExceptionIsAssertInconclusiveExc ExpectedException = expectedException, }; - DummyTestClass.TestMethodBody = o => throw new UTF.AssertInconclusiveException(); - UTF.TestResult result = method.Invoke(null); + DummyTestClass.TestMethodBody = o => throw new AssertInconclusiveException(); + TestResult result = await method.InvokeAsync(null); string message = "Exception of type 'Microsoft.VisualStudio.TestTools.UnitTesting.AssertInconclusiveException' was thrown."; Verify(result.TestFailureException!.Message == message); Verify(result.Outcome == UTF.UnitTestOutcome.Inconclusive); } - public void VerifyShouldThrowIfThrownExceptionIsNotSameAsExpectedException() + public async Task VerifyShouldThrowIfThrownExceptionIsNotSameAsExpectedException() { - UTF.ExpectedExceptionAttribute expectedException = new(typeof(Exception)); + ExpectedExceptionAttribute expectedException = new(typeof(Exception)); var method = new TestMethodInfo( _methodInfo, _testClassInfo, @@ -1366,16 +1369,16 @@ public void VerifyShouldThrowIfThrownExceptionIsNotSameAsExpectedException() }; DummyTestClass.TestMethodBody = o => throw new DivideByZeroException(); - UTF.TestResult result = method.Invoke(null); + TestResult result = await method.InvokeAsync(null); string message = "Test method threw exception System.DivideByZeroException, but exception System.Exception was expected. " + "Exception message: System.DivideByZeroException: Attempted to divide by zero."; Verify(result.TestFailureException!.Message == message); Verify(result.Outcome == UTF.UnitTestOutcome.Failed); } - public void VerifyShouldRethrowIfThrownExceptionIsAssertExceptionWhichIsNotSameAsExpectedException() + public async Task VerifyShouldRethrowIfThrownExceptionIsAssertExceptionWhichIsNotSameAsExpectedException() { - UTF.ExpectedExceptionAttribute expectedException = new(typeof(Exception)); + ExpectedExceptionAttribute expectedException = new(typeof(Exception)); var method = new TestMethodInfo( _methodInfo, _testClassInfo, @@ -1386,8 +1389,8 @@ public void VerifyShouldRethrowIfThrownExceptionIsAssertExceptionWhichIsNotSameA ExpectedException = expectedException, }; - DummyTestClass.TestMethodBody = o => throw new UTF.AssertInconclusiveException(); - UTF.TestResult result = method.Invoke(null); + DummyTestClass.TestMethodBody = o => throw new AssertInconclusiveException(); + TestResult result = await method.InvokeAsync(null); string message = "Exception of type 'Microsoft.VisualStudio.TestTools.UnitTesting.AssertInconclusiveException' was thrown."; Verify(result.TestFailureException!.Message == message); Verify(result.Outcome == UTF.UnitTestOutcome.Inconclusive); @@ -1398,9 +1401,9 @@ public void ResolveExpectedExceptionShouldThrowWhenAttributeIsDefinedTwice_Diffe MethodInfo testMethodInfo = typeof(DummyTestClassForExpectedException).GetMethod(nameof(DummyTestClassForExpectedException.DummyTestMethod1))!; TestClassInfo classInfo = new( typeof(DummyTestClassForExpectedException), - typeof(DummyTestClassForExpectedException).GetConstructor(Array.Empty())!, + typeof(DummyTestClassForExpectedException).GetConstructor([])!, isParameterlessConstructor: true, - new UTF.TestClassAttribute(), + new TestClassAttribute(), new TestAssemblyInfo(typeof(DummyTestClassForExpectedException).Assembly)); TypeInspectionException ex = UTF.Assert.ThrowsException(() => new TestMethodInfo(testMethodInfo, classInfo, _testContextImplementation) @@ -1416,9 +1419,9 @@ public void ResolveExpectedExceptionShouldThrowWhenAttributeIsDefinedTwice_SameC MethodInfo testMethodInfo = typeof(DummyTestClassForExpectedException).GetMethod(nameof(DummyTestClassForExpectedException.DummyTestMethod1))!; TestClassInfo classInfo = new( typeof(DummyTestClassForExpectedException), - typeof(DummyTestClassForExpectedException).GetConstructor(Array.Empty())!, + typeof(DummyTestClassForExpectedException).GetConstructor([])!, isParameterlessConstructor: true, - new UTF.TestClassAttribute(), + new TestClassAttribute(), new TestAssemblyInfo(typeof(DummyTestClassForExpectedException).Assembly)); TypeInspectionException ex = UTF.Assert.ThrowsException(() => new TestMethodInfo(testMethodInfo, classInfo, _testContextImplementation) @@ -1435,9 +1438,9 @@ public void ResolveExpectedExceptionHelperShouldReturnExpectedExceptionAttribute MethodInfo methodInfo = type.GetMethod(nameof(DummyTestClassForExpectedException.TestMethodWithExpectedException))!; TestClassInfo classInfo = new( typeof(DummyTestClassForExpectedException), - typeof(DummyTestClassForExpectedException).GetConstructor(Array.Empty())!, + typeof(DummyTestClassForExpectedException).GetConstructor([])!, isParameterlessConstructor: true, - new UTF.TestClassAttribute(), + new TestClassAttribute(), new TestAssemblyInfo(typeof(DummyTestClassForExpectedException).Assembly)); var testMethodInfo = new TestMethodInfo(methodInfo, classInfo, _testContextImplementation) @@ -1447,7 +1450,7 @@ public void ResolveExpectedExceptionHelperShouldReturnExpectedExceptionAttribute }; Verify(testMethodInfo.ExpectedException is not null); - Verify(((UTF.ExpectedExceptionAttribute)testMethodInfo.ExpectedException).ExceptionType == typeof(DivideByZeroException)); + Verify(((ExpectedExceptionAttribute)testMethodInfo.ExpectedException).ExceptionType == typeof(DivideByZeroException)); } public void ResolveExpectedExceptionHelperShouldReturnNullIfExpectedExceptionAttributeIsNotPresent() @@ -1456,9 +1459,9 @@ public void ResolveExpectedExceptionHelperShouldReturnNullIfExpectedExceptionAtt MethodInfo methodInfo = type.GetMethod(nameof(DummyTestClassForExpectedException.TestMethodWithoutExpectedException))!; TestClassInfo classInfo = new( typeof(DummyTestClassForExpectedException), - typeof(DummyTestClassForExpectedException).GetConstructor(Array.Empty())!, + typeof(DummyTestClassForExpectedException).GetConstructor([])!, isParameterlessConstructor: true, - new UTF.TestClassAttribute(), + new TestClassAttribute(), new TestAssemblyInfo(typeof(DummyTestClassForExpectedException).Assembly)); var testMethodInfo = new TestMethodInfo(methodInfo, classInfo, _testContextImplementation) @@ -1474,7 +1477,7 @@ public void ResolveExpectedExceptionHelperShouldReturnNullIfExpectedExceptionAtt #region TestMethod invoke setup order - public void TestMethodInfoInvokeShouldInitializeClassInstanceTestInitializeAndTestCleanupInOrder() + public async Task TestMethodInfoInvokeShouldInitializeClassInstanceTestInitializeAndTestCleanupInOrder() { var callOrder = new List(); _testClassInfo.TestInitializeMethod = typeof(DummyTestClass).GetMethod("DummyTestInitializeMethod")!; @@ -1486,7 +1489,7 @@ public void TestMethodInfoInvokeShouldInitializeClassInstanceTestInitializeAndTe DummyTestClass.TestMethodBody = classInstance => callOrder.Add("testMethodInfo"); DummyTestClass.TestCleanupMethodBody = classInstance => callOrder.Add("testCleanup"); - UTF.TestResult result = _testMethodInfo.Invoke(null); + TestResult result = await _testMethodInfo.InvokeAsync(null); var expectedCallOrder = new List { @@ -1504,11 +1507,11 @@ public void TestMethodInfoInvokeShouldInitializeClassInstanceTestInitializeAndTe #region TestMethod timeout scenarios - public void TestMethodInfoInvokeShouldReturnTestFailureOnTimeout() + public async Task TestMethodInfoInvokeShouldReturnTestFailureOnTimeout() { var testablePlatformServiceProvider = new TestablePlatformServiceProvider(); - RunWithTestablePlatformService(testablePlatformServiceProvider, () => + await RunWithTestablePlatformService(testablePlatformServiceProvider, async () => { testablePlatformServiceProvider.MockThreadOperations.CallBase = true; @@ -1522,14 +1525,14 @@ public void TestMethodInfoInvokeShouldReturnTestFailureOnTimeout() Executor = _testMethodAttribute, }; - UTF.TestResult result = method.Invoke(null); + TestResult result = await method.InvokeAsync(null); Verify(result.Outcome == UTF.UnitTestOutcome.Timeout); Verify(result.TestFailureException!.Message.Equals("Test 'DummyTestMethod' timed out after 1ms", StringComparison.Ordinal)); }); } - public void TestMethodInfoInvokeShouldReturnTestPassedOnCompletionWithinTimeout() + public async Task TestMethodInfoInvokeShouldReturnTestPassedOnCompletionWithinTimeout() { DummyTestClass.TestMethodBody = o => { /* do nothing */ }; var method = new TestMethodInfo(_methodInfo, _testClassInfo, _testContextImplementation) @@ -1537,14 +1540,14 @@ public void TestMethodInfoInvokeShouldReturnTestPassedOnCompletionWithinTimeout( TimeoutInfo = TimeoutInfo.FromTimeout(3600 * 1000), Executor = _testMethodAttribute, }; - UTF.TestResult result = method.Invoke(null); + TestResult result = await method.InvokeAsync(null); Verify(result.Outcome == UTF.UnitTestOutcome.Passed); } - public void TestMethodInfoInvokeShouldCancelTokenSourceOnTimeout() + public async Task TestMethodInfoInvokeShouldCancelTokenSourceOnTimeout() { var testablePlatformServiceProvider = new TestablePlatformServiceProvider(); - RunWithTestablePlatformService(testablePlatformServiceProvider, () => + await RunWithTestablePlatformService(testablePlatformServiceProvider, async () => { testablePlatformServiceProvider.MockThreadOperations.CallBase = true; PlatformServiceProvider.Instance = testablePlatformServiceProvider; @@ -1557,7 +1560,7 @@ public void TestMethodInfoInvokeShouldCancelTokenSourceOnTimeout() TimeoutInfo = TimeoutInfo.FromTimeout(1), Executor = _testMethodAttribute, }; - UTF.TestResult result = method.Invoke(null); + TestResult result = await method.InvokeAsync(null); Verify(result.Outcome == UTF.UnitTestOutcome.Timeout); Verify(result.TestFailureException!.Message.Equals("Test 'DummyTestMethod' timed out after 1ms", StringComparison.Ordinal)); @@ -1565,10 +1568,10 @@ public void TestMethodInfoInvokeShouldCancelTokenSourceOnTimeout() }); } - public void TestMethodInfoInvokeShouldFailOnTokenSourceCancellation() + public async Task TestMethodInfoInvokeShouldFailOnTokenSourceCancellation() { var testablePlatformServiceProvider = new TestablePlatformServiceProvider(); - RunWithTestablePlatformService(testablePlatformServiceProvider, () => + await RunWithTestablePlatformService(testablePlatformServiceProvider, async () => { testablePlatformServiceProvider.MockThreadOperations.CallBase = true; PlatformServiceProvider.Instance = testablePlatformServiceProvider; @@ -1591,7 +1594,7 @@ public void TestMethodInfoInvokeShouldFailOnTokenSourceCancellation() TimeoutInfo = TimeoutInfo.FromTimeout(100000), Executor = _testMethodAttribute, }; - UTF.TestResult result = method.Invoke(null); + TestResult result = await method.InvokeAsync(null); Verify(result.Outcome == UTF.UnitTestOutcome.Timeout); Verify(result.TestFailureException!.Message.Equals("Test 'DummyTestMethod' was canceled", StringComparison.Ordinal)); @@ -1733,7 +1736,7 @@ public void ResolveArgumentsShouldReturnPopulatedParamsWithAllProvided() #region helper methods - private static void RunWithTestablePlatformService(TestablePlatformServiceProvider testablePlatformServiceProvider, Action action) + private static async Task RunWithTestablePlatformService(TestablePlatformServiceProvider testablePlatformServiceProvider, Func action) { try { @@ -1742,7 +1745,7 @@ private static void RunWithTestablePlatformService(TestablePlatformServiceProvid Returns(true). Callback((Action a, int timeout, CancellationToken token) => a.Invoke()); - action.Invoke(); + await action(); } finally { @@ -1762,11 +1765,11 @@ public class DummyTestClassBase public class DummyTestClass : DummyTestClassBase { - private static UTFExtension.TestContext s_tc = null!; + private static TestContext s_tc = null!; public DummyTestClass() => TestConstructorMethodBody(); - public DummyTestClass(UTFExtension.TestContext tc) => Verify(tc is not null); + public DummyTestClass(TestContext tc) => Verify(tc is not null); public static Action TestConstructorMethodBody { get; set; } = null!; @@ -1784,9 +1787,9 @@ public class DummyTestClass : DummyTestClassBase public static Func DummyAsyncTestMethodBody { get; set; } = null!; - public static UTFExtension.TestContext GetTestContext() => s_tc; + public static TestContext GetTestContext() => s_tc; - public UTFExtension.TestContext TestContext + public TestContext TestContext { get => throw new NotImplementedException(); @@ -1836,7 +1839,7 @@ public DummyTestClassWithParameterizedCtor(int x) public class DummyTestClassWithTestContextWithoutSetter { - public UTFExtension.TestContext TestContext { get; } = null!; + public TestContext TestContext => null!; } public class DummyTestClassWithDisposable : IDisposable @@ -1859,7 +1862,7 @@ public void DummyTestMethod() /// /// Custom Expected exception attribute which overrides the Verify method. /// - public class CustomExpectedExceptionAttribute : UTF.ExpectedExceptionBaseAttribute + public class CustomExpectedExceptionAttribute : ExpectedExceptionBaseAttribute { public CustomExpectedExceptionAttribute(Type exceptionType, string noExceptionMessage) : base(noExceptionMessage) => ExceptionType = exceptionType; @@ -1871,9 +1874,9 @@ public CustomExpectedExceptionAttribute(Type exceptionType, string noExceptionMe protected internal override void Verify(Exception exception) { IsVerifyInvoked = true; - if (exception is UTF.AssertInconclusiveException) + if (exception is AssertInconclusiveException) { - throw new UTF.AssertInconclusiveException(); + throw new AssertInconclusiveException(); } else if (!exception.Message.Contains(NoExceptionMessage)) { @@ -1897,9 +1900,9 @@ public DerivedCustomExpectedExceptionAttribute(Type exceptionType, string noExce protected internal override void Verify(Exception exception) { IsVerifyInvoked = true; - if (exception is UTF.AssertInconclusiveException) + if (exception is AssertInconclusiveException) { - throw new UTF.AssertInconclusiveException(); + throw new AssertInconclusiveException(); } else if (!exception.Message.Contains(NoExceptionMessage)) { @@ -1912,18 +1915,18 @@ protected internal override void Verify(Exception exception) public class DummyTestClassForExpectedException { - private class MyExpectedException1Attribute : UTF.ExpectedExceptionBaseAttribute + private class MyExpectedException1Attribute : ExpectedExceptionBaseAttribute { protected internal override void Verify(Exception exception) => throw new NotImplementedException(); } [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] - public class MyExpectedException2Attribute : UTF.ExpectedExceptionBaseAttribute + public class MyExpectedException2Attribute : ExpectedExceptionBaseAttribute { protected internal override void Verify(Exception exception) => throw new NotImplementedException(); } - [UTF.ExpectedException(typeof(Exception))] + [ExpectedException(typeof(Exception))] [MyExpectedException1] public void DummyTestMethod1() @@ -1936,7 +1939,7 @@ public void DummyTestMethod2() { } - [UTF.ExpectedException(typeof(DivideByZeroException))] + [ExpectedException(typeof(DivideByZeroException))] public void TestMethodWithExpectedException() { } diff --git a/test/UnitTests/MSTestAdapter.UnitTests/Execution/TestMethodRunnerTests.cs b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Execution/TestMethodRunnerTests.cs similarity index 97% rename from test/UnitTests/MSTestAdapter.UnitTests/Execution/TestMethodRunnerTests.cs rename to test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Execution/TestMethodRunnerTests.cs index edb63ea7ab..2b2c91a3d9 100644 --- a/test/UnitTests/MSTestAdapter.UnitTests/Execution/TestMethodRunnerTests.cs +++ b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Execution/TestMethodRunnerTests.cs @@ -8,6 +8,7 @@ using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices; using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface; using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.UnitTests.TestableImplementations; +using Microsoft.VisualStudio.TestTools.UnitTesting.Resources; using Moq; @@ -266,7 +267,7 @@ public async Task RunTestMethodShouldSetDataRowIndexForDataDrivenTestsWhenDataIs // Setup mocks _testablePlatformServiceProvider.MockReflectionOperations.Setup(rf => rf.GetCustomAttributes(methodInfo, It.IsAny())).Returns(attributes); - var testMethodInfo = new TestableTestMethodInfo(methodInfo, _testClassInfo, _testMethodOptions, () => new UTF.TestResult()); + var testMethodInfo = new TestableTestMethodInfo(methodInfo, _testClassInfo, _testMethodOptions, () => new TestResult()); var testMethodRunner = new TestMethodRunner(testMethodInfo, _testMethod, _testContextImplementation); _testablePlatformServiceProvider.MockTestDataSource.Setup(tds => tds.GetData(testMethodInfo, _testContextImplementation)).Returns([1, 2, 3]); @@ -293,7 +294,7 @@ public async Task RunTestMethodShouldRunOnlyDataSourceTestsWhenBothDataSourceAnd // Setup mocks _testablePlatformServiceProvider.MockReflectionOperations.Setup(rf => rf.GetCustomAttributes(_methodInfo, It.IsAny())).Returns(attributes); - var testMethodInfo = new TestableTestMethodInfo(_methodInfo, _testClassInfo, _testMethodOptions, () => new UTF.TestResult()); + var testMethodInfo = new TestableTestMethodInfo(_methodInfo, _testClassInfo, _testMethodOptions, () => new TestResult()); var testMethodRunner = new TestMethodRunner(testMethodInfo, _testMethod, _testContextImplementation); _testablePlatformServiceProvider.MockTestDataSource.Setup(tds => tds.GetData(testMethodInfo, _testContextImplementation)).Returns([1, 2, 3]); @@ -359,7 +360,7 @@ public async Task RunTestMethodShouldSetResultFilesIfPresentForDataDrivenTests() { TestResult testResult = new() { - ResultFiles = new List { "C:\\temp.txt" }, + ResultFiles = ["C:\\temp.txt"], }; int dummyIntData1 = 1; @@ -388,7 +389,7 @@ public async Task RunTestMethodWithEmptyDataSourceShouldNotFailBecauseConsiderEm private async Task RunTestMethodWithEmptyDataSourceShouldFailIfConsiderEmptyDataSourceAsInconclusiveIsNotTrueHelper(bool considerEmptyAsInconclusive) { - Mock? existingMock = _testablePlatformServiceProvider.MockReflectionOperations; + Mock? existingMock = _testablePlatformServiceProvider.MockReflectionOperations; try { // We want this test to go through the "real" reflection to hit the product code path relevant for the test. @@ -459,7 +460,7 @@ private class TestableTestMethodInfo : TestMethodInfo { private readonly Func _invokeTest; - internal TestableTestMethodInfo(MethodInfo testMethod, TestClassInfo parent, TestMethodOptions testMethodOptions, Func invoke) + internal TestableTestMethodInfo(MethodInfo testMethod, TestClassInfo parent, TestMethodOptions testMethodOptions, Func invoke) : base(testMethod, parent, testMethodOptions.TestContext) { TimeoutInfo = testMethodOptions.TimeoutInfo; @@ -532,12 +533,12 @@ public DummyTestClassWithParameterizedCtor(int x) public class DummyTestClassWithTestContextWithoutSetter { - public TestContext TestContext { get; } = null!; + public TestContext TestContext => null!; } public class DummyTestClassEmptyDataSource { - public static IEnumerable EmptyProperty => Array.Empty(); + public static IEnumerable EmptyProperty => []; [DynamicData("EmptyProperty")] public void TestMethod(int x) diff --git a/test/UnitTests/MSTestAdapter.UnitTests/Execution/TestPropertyAttributeTests.cs b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Execution/TestPropertyAttributeTests.cs similarity index 93% rename from test/UnitTests/MSTestAdapter.UnitTests/Execution/TestPropertyAttributeTests.cs rename to test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Execution/TestPropertyAttributeTests.cs index a61975dfb5..a2c6b1aafe 100644 --- a/test/UnitTests/MSTestAdapter.UnitTests/Execution/TestPropertyAttributeTests.cs +++ b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Execution/TestPropertyAttributeTests.cs @@ -60,7 +60,7 @@ public void GetTestMethodInfoShouldAddPropertiesFromContainingClassCorrectly() Assert.IsTrue(testContext.TryGetPropertyValue("DummyTestClassBaseKey2", out object? value3)); Assert.AreEqual("DummyTestClassBaseValue2", value3); - TestPlatform.ObjectModel.Trait[] traits = ReflectHelper.Instance.GetTestPropertiesAsTraits(typeof(DummyTestClassBase).GetMethod(nameof(DummyTestClassBase.VirtualTestMethodInBaseAndDerived))!).ToArray(); + TestPlatform.ObjectModel.Trait[] traits = [.. ReflectHelper.Instance.GetTestPropertiesAsTraits(typeof(DummyTestClassBase).GetMethod(nameof(DummyTestClassBase.VirtualTestMethodInBaseAndDerived))!)]; Assert.AreEqual(3, traits.Length); Assert.AreEqual("TestMethodKeyFromBase", traits[0].Name); Assert.AreEqual("TestMethodValueFromBase", traits[0].Value); @@ -99,7 +99,7 @@ public void GetTestMethodInfoShouldAddPropertiesFromContainingClassAndBaseClasse Assert.IsTrue(testContext.TryGetPropertyValue("DummyTestClassBaseKey2", out object? value6)); Assert.AreEqual("DummyTestClassBaseValue2", value6); - TestPlatform.ObjectModel.Trait[] traits = ReflectHelper.Instance.GetTestPropertiesAsTraits(typeof(DummyTestClassDerived).GetMethod(nameof(DummyTestClassDerived.VirtualTestMethodInBaseAndDerived))!).ToArray(); + TestPlatform.ObjectModel.Trait[] traits = [.. ReflectHelper.Instance.GetTestPropertiesAsTraits(typeof(DummyTestClassDerived).GetMethod(nameof(DummyTestClassDerived.VirtualTestMethodInBaseAndDerived))!)]; Assert.AreEqual(6, traits.Length); Assert.AreEqual("DerivedMethod1Key", traits[0].Name); Assert.AreEqual("DerivedMethod1Value", traits[0].Value); @@ -144,7 +144,7 @@ public void GetTestMethodInfoShouldAddPropertiesFromContainingClassAndBaseClasse Assert.IsTrue(testContext.TryGetPropertyValue("DummyTestClassBaseKey2", out object? value6)); Assert.AreEqual("DummyTestClassBaseValue2", value6); - TestPlatform.ObjectModel.Trait[] traits = ReflectHelper.Instance.GetTestPropertiesAsTraits(typeof(DummyTestClassDerived).GetMethod(nameof(DummyTestClassDerived.VirtualTestMethodInDerivedButNotTestMethodInBase))!).ToArray(); + TestPlatform.ObjectModel.Trait[] traits = [.. ReflectHelper.Instance.GetTestPropertiesAsTraits(typeof(DummyTestClassDerived).GetMethod(nameof(DummyTestClassDerived.VirtualTestMethodInDerivedButNotTestMethodInBase))!)]; Assert.AreEqual(6, traits.Length); Assert.AreEqual("DerivedMethod2Key", traits[0].Name); Assert.AreEqual("DerivedMethod2Value", traits[0].Value); diff --git a/test/UnitTests/MSTestAdapter.UnitTests/Execution/TypeCacheTests.cs b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Execution/TypeCacheTests.cs similarity index 80% rename from test/UnitTests/MSTestAdapter.UnitTests/Execution/TypeCacheTests.cs rename to test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Execution/TypeCacheTests.cs index a1348ff357..4b3a1cd12b 100644 --- a/test/UnitTests/MSTestAdapter.UnitTests/Execution/TypeCacheTests.cs +++ b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Execution/TypeCacheTests.cs @@ -152,7 +152,7 @@ public void GetTestMethodInfoShouldSetTestContextIfPresent() var testMethod = new TestMethod(methodInfo.Name, type.FullName!, "A", isAsync: false); _mockReflectHelper.Setup( - rh => rh.IsNonDerivedAttributeDefined(type, true)).Returns(true); + rh => rh.IsAttributeDefined(type, true)).Returns(true); TestMethodInfo? testMethodInfo = _typeCache.GetTestMethodInfo( testMethod, @@ -169,7 +169,7 @@ public void GetTestMethodInfoShouldSetTestContextToNullIfNotPresent() var testMethod = new TestMethod(methodInfo.Name, type.FullName!, "A", isAsync: false); _mockReflectHelper.Setup( - rh => rh.IsNonDerivedAttributeDefined(type, true)).Returns(true); + rh => rh.IsAttributeDefined(type, true)).Returns(true); TestMethodInfo? testMethodInfo = _typeCache.GetTestMethodInfo( testMethod, @@ -188,7 +188,7 @@ public void GetTestMethodInfoShouldAddAssemblyInfoToTheCache() var testMethod = new TestMethod(methodInfo.Name, type.FullName!, "A", isAsync: false); _mockReflectHelper.Setup( - rh => rh.IsNonDerivedAttributeDefined(type, true)).Returns(true); + rh => rh.IsAttributeDefined(type, true)).Returns(true); _typeCache.GetTestMethodInfo( testMethod, @@ -204,10 +204,10 @@ public void GetTestMethodInfoShouldNotThrowIfWeFailToDiscoverTypeFromAnAssembly( var testMethod = new TestMethod(methodInfo.Name, type.FullName!, "A", isAsync: false); _mockReflectHelper.Setup( - rh => rh.IsNonDerivedAttributeDefined(It.IsAny(), true)).Throws(new Exception()); + rh => rh.IsAttributeDefined(It.IsAny(), true)).Throws(new Exception()); _mockReflectHelper.Setup( - rh => rh.IsNonDerivedAttributeDefined(typeof(DummyTestClassWithTestMethods), true)).Returns(true); + rh => rh.IsAttributeDefined(typeof(DummyTestClassWithTestMethods), true)).Returns(true); _typeCache.GetTestMethodInfo( testMethod, @@ -222,10 +222,10 @@ public void GetTestMethodInfoShouldCacheAssemblyInitializeAttribute() var testMethod = new TestMethod("TestInit", type.FullName!, "A", isAsync: false); _mockReflectHelper.Setup( - rh => rh.IsDerivedAttributeDefined(type, false)).Returns(true); + rh => rh.IsAttributeDefined(type, false)).Returns(true); _mockReflectHelper.Setup( - rh => rh.IsNonDerivedAttributeDefined(type.GetMethod("AssemblyInit")!, false)).Returns(true); + rh => rh.IsAttributeDefined(type.GetMethod("AssemblyInit")!, false)).Returns(true); _typeCache.GetTestMethodInfo( testMethod, @@ -241,10 +241,10 @@ public void GetTestMethodInfoShouldCacheAssemblyCleanupAttribute() var testMethod = new TestMethod("TestCleanup", type.FullName!, "A", isAsync: false); _mockReflectHelper.Setup( - rh => rh.IsDerivedAttributeDefined(type, false)).Returns(true); + rh => rh.IsAttributeDefined(type, false)).Returns(true); _mockReflectHelper.Setup( - rh => rh.IsNonDerivedAttributeDefined(type.GetMethod("AssemblyCleanup")!, false)).Returns(true); + rh => rh.IsAttributeDefined(type.GetMethod("AssemblyCleanup")!, false)).Returns(true); _typeCache.GetTestMethodInfo( testMethod, @@ -260,12 +260,12 @@ public void GetTestMethodInfoShouldCacheAssemblyInitAndCleanupAttribute() var testMethod = new TestMethod("TestInitOrCleanup", type.FullName!, "A", isAsync: false); _mockReflectHelper.Setup( - rh => rh.IsDerivedAttributeDefined(type, false)).Returns(true); + rh => rh.IsAttributeDefined(type, false)).Returns(true); _mockReflectHelper.Setup( - rh => rh.IsNonDerivedAttributeDefined(type.GetMethod("AssemblyInit")!, false)).Returns(true); + rh => rh.IsAttributeDefined(type.GetMethod("AssemblyInit")!, false)).Returns(true); _mockReflectHelper.Setup( - rh => rh.IsNonDerivedAttributeDefined(type.GetMethod("AssemblyCleanup")!, false)).Returns(true); + rh => rh.IsAttributeDefined(type.GetMethod("AssemblyCleanup")!, false)).Returns(true); _typeCache.GetTestMethodInfo( testMethod, @@ -282,10 +282,10 @@ public void GetTestMethodInfoShouldThrowIfAssemblyInitHasIncorrectSignature() var testMethod = new TestMethod("M", type.FullName!, "A", isAsync: false); _mockReflectHelper.Setup( - rh => rh.IsDerivedAttributeDefined(type, false)).Returns(true); + rh => rh.IsAttributeDefined(type, false)).Returns(true); _mockReflectHelper.Setup( - rh => rh.IsNonDerivedAttributeDefined(type.GetMethod("AssemblyInit")!, false)).Returns(true); + rh => rh.IsAttributeDefined(type.GetMethod("AssemblyInit")!, false)).Returns(true); void A() => _typeCache.GetTestMethodInfo( @@ -311,10 +311,10 @@ public void GetTestMethodInfoShouldThrowIfAssemblyCleanupHasIncorrectSignature() var testMethod = new TestMethod("M", type.FullName!, "A", isAsync: false); _mockReflectHelper.Setup( - rh => rh.IsDerivedAttributeDefined(type, false)).Returns(true); + rh => rh.IsAttributeDefined(type, false)).Returns(true); _mockReflectHelper.Setup( - rh => rh.IsNonDerivedAttributeDefined(type.GetMethod("AssemblyCleanup")!, false)).Returns(true); + rh => rh.IsAttributeDefined(type.GetMethod("AssemblyCleanup")!, false)).Returns(true); void A() => _typeCache.GetTestMethodInfo( @@ -341,7 +341,7 @@ public void GetTestMethodInfoShouldCacheAssemblyInfoInstanceAndReuseTheCache() var testMethod = new TestMethod(methodInfo.Name, type.FullName!, "A", isAsync: false); _mockReflectHelper.Setup( - rh => rh.IsDerivedAttributeDefined(type, false)).Returns(true); + rh => rh.IsAttributeDefined(type, false)).Returns(true); _typeCache.GetTestMethodInfo( testMethod, @@ -351,7 +351,7 @@ public void GetTestMethodInfoShouldCacheAssemblyInfoInstanceAndReuseTheCache() testMethod, new TestContextImplementation(testMethod, new ThreadSafeStringWriter(null!, "test"), new Dictionary())); - _mockReflectHelper.Verify(rh => rh.IsDerivedAttributeDefined(type, false), Times.Once); + _mockReflectHelper.Verify(rh => rh.IsAttributeDefined(type, false), Times.Once); Verify(_typeCache.AssemblyInfoCache.Count == 1); } @@ -366,7 +366,7 @@ public void GetTestMethodInfoShouldAddClassInfoToTheCache() var testMethod = new TestMethod(methodInfo.Name, type.FullName!, "A", isAsync: false); _mockReflectHelper.Setup( - rh => rh.IsNonDerivedAttributeDefined(type, true)).Returns(true); + rh => rh.IsAttributeDefined(type, true)).Returns(true); _typeCache.GetTestMethodInfo( testMethod, @@ -383,10 +383,10 @@ public void GetTestMethodInfoShouldCacheClassInitializeAttribute() var testMethod = new TestMethod("TestInit", type.FullName!, "A", isAsync: false); _mockReflectHelper.Setup( - rh => rh.IsNonDerivedAttributeDefined(type, true)).Returns(true); + rh => rh.IsAttributeDefined(type, true)).Returns(true); _mockReflectHelper.Setup( - rh => rh.IsNonDerivedAttributeDefined(type.GetMethod("AssemblyInit")!, false)).Returns(true); + rh => rh.IsAttributeDefined(type.GetMethod("AssemblyInit")!, false)).Returns(true); _typeCache.GetTestMethodInfo( testMethod, @@ -405,16 +405,16 @@ public void GetTestMethodInfoShouldCacheBaseClassInitializeAttributes() var testMethod = new TestMethod("TestMethod", type.FullName!, "A", false); _mockReflectHelper.Setup( - rh => rh.IsDerivedAttributeDefined(type, false)).Returns(true); + rh => rh.IsAttributeDefined(type, false)).Returns(true); _mockReflectHelper.Setup( - rh => rh.IsNonDerivedAttributeDefined(baseType.GetMethod("AssemblyInit")!, false)).Returns(true); + rh => rh.IsAttributeDefined(baseType.GetMethod("AssemblyInit")!, false)).Returns(true); _mockReflectHelper.Setup( - rh => rh.GetFirstDerivedAttributeOrDefault(baseType.GetMethod("AssemblyInit")!, true)) + rh => rh.GetFirstAttributeOrDefault(baseType.GetMethod("AssemblyInit")!, true)) .Returns(new ClassInitializeAttribute(InheritanceBehavior.BeforeEachDerivedClass)); _mockReflectHelper.Setup( - rh => rh.IsNonDerivedAttributeDefined(type.GetMethod("ClassInit")!, false)).Returns(true); + rh => rh.IsAttributeDefined(type.GetMethod("ClassInit")!, false)).Returns(true); _typeCache.GetTestMethodInfo( testMethod, @@ -431,10 +431,10 @@ public void GetTestMethodInfoShouldCacheClassCleanupAttribute() var testMethod = new TestMethod("TestCleanup", type.FullName!, "A", isAsync: false); _mockReflectHelper.Setup( - rh => rh.IsNonDerivedAttributeDefined(type, true)).Returns(true); + rh => rh.IsAttributeDefined(type, true)).Returns(true); _mockReflectHelper.Setup( - rh => rh.IsNonDerivedAttributeDefined(type.GetMethod("AssemblyCleanup")!, false)).Returns(true); + rh => rh.IsAttributeDefined(type.GetMethod("AssemblyCleanup")!, false)).Returns(true); _typeCache.GetTestMethodInfo( testMethod, @@ -452,11 +452,11 @@ public void GetTestMethodInfoShouldCacheBaseClassCleanupAttributes() var testMethod = new TestMethod("TestMethod", type.FullName!, "A", false); _mockReflectHelper.Setup( - rh => rh.IsDerivedAttributeDefined(type, false)).Returns(true); + rh => rh.IsAttributeDefined(type, false)).Returns(true); _mockReflectHelper.Setup( - rh => rh.IsNonDerivedAttributeDefined(baseType.GetMethod("AssemblyCleanup")!, false)).Returns(true); + rh => rh.IsAttributeDefined(baseType.GetMethod("AssemblyCleanup")!, false)).Returns(true); _mockReflectHelper.Setup( - rh => rh.GetFirstDerivedAttributeOrDefault(baseType.GetMethod("AssemblyCleanup")!, true)) + rh => rh.GetFirstAttributeOrDefault(baseType.GetMethod("AssemblyCleanup")!, true)) .Returns(new ClassCleanupAttribute(InheritanceBehavior.BeforeEachDerivedClass)); _typeCache.GetTestMethodInfo( @@ -474,11 +474,11 @@ public void GetTestMethodInfoShouldCacheClassInitAndCleanupAttribute() var testMethod = new TestMethod("TestInitOrCleanup", type.FullName!, "A", isAsync: false); _mockReflectHelper.Setup( - rh => rh.IsNonDerivedAttributeDefined(type, true)).Returns(true); + rh => rh.IsAttributeDefined(type, true)).Returns(true); _mockReflectHelper.Setup( - rh => rh.IsNonDerivedAttributeDefined(type.GetMethod("AssemblyInit")!, false)).Returns(true); + rh => rh.IsAttributeDefined(type.GetMethod("AssemblyInit")!, false)).Returns(true); _mockReflectHelper.Setup( - rh => rh.IsNonDerivedAttributeDefined(type.GetMethod("AssemblyCleanup")!, false)).Returns(true); + rh => rh.IsAttributeDefined(type.GetMethod("AssemblyCleanup")!, false)).Returns(true); _typeCache.GetTestMethodInfo( testMethod, @@ -499,23 +499,23 @@ public void GetTestMethodInfoShouldCacheBaseClassInitAndCleanupAttributes() MethodInfo baseCleanupMethod = baseType.GetMethod("ClassCleanup")!; _mockReflectHelper.Setup( - rh => rh.IsDerivedAttributeDefined(type, false)).Returns(true); + rh => rh.IsAttributeDefined(type, false)).Returns(true); _mockReflectHelper.Setup( - rh => rh.IsNonDerivedAttributeDefined(baseInitializeMethod, false)).Returns(true); + rh => rh.IsAttributeDefined(baseInitializeMethod, false)).Returns(true); _mockReflectHelper.Setup( - rh => rh.GetFirstDerivedAttributeOrDefault(baseInitializeMethod, true)) + rh => rh.GetFirstAttributeOrDefault(baseInitializeMethod, true)) .Returns(new ClassInitializeAttribute(InheritanceBehavior.BeforeEachDerivedClass)); _mockReflectHelper.Setup( - rh => rh.IsNonDerivedAttributeDefined(baseCleanupMethod, false)).Returns(true); + rh => rh.IsAttributeDefined(baseCleanupMethod, false)).Returns(true); _mockReflectHelper.Setup( - rh => rh.GetFirstDerivedAttributeOrDefault(baseCleanupMethod, true)) + rh => rh.GetFirstAttributeOrDefault(baseCleanupMethod, true)) .Returns(new ClassCleanupAttribute(InheritanceBehavior.BeforeEachDerivedClass)); _mockReflectHelper.Setup( - rh => rh.IsNonDerivedAttributeDefined(type.GetMethod("AssemblyInit")!, false)).Returns(true); + rh => rh.IsAttributeDefined(type.GetMethod("AssemblyInit")!, false)).Returns(true); _mockReflectHelper.Setup( - rh => rh.IsNonDerivedAttributeDefined(type.GetMethod("AssemblyCleanup")!, false)).Returns(true); + rh => rh.IsAttributeDefined(type.GetMethod("AssemblyCleanup")!, false)).Returns(true); _typeCache.GetTestMethodInfo( testMethod, @@ -541,35 +541,35 @@ public void GetTestMethodInfoShouldCacheParentAndGrandparentClassInitAndCleanupA MethodInfo parentCleanupMethod = parentType.GetMethod("ChildClassCleanup")!; _mockReflectHelper - .Setup(rh => rh.IsNonDerivedAttributeDefined(type, true)) + .Setup(rh => rh.IsAttributeDefined(type, true)) .Returns(true); // Setup grandparent class init/cleanup methods _mockReflectHelper - .Setup(rh => rh.IsNonDerivedAttributeDefined(grandparentInitMethod, false)) + .Setup(rh => rh.IsAttributeDefined(grandparentInitMethod, false)) .Returns(true); _mockReflectHelper - .Setup(rh => rh.GetFirstDerivedAttributeOrDefault(grandparentInitMethod, true)) + .Setup(rh => rh.GetFirstAttributeOrDefault(grandparentInitMethod, true)) .Returns(new ClassInitializeAttribute(InheritanceBehavior.BeforeEachDerivedClass)); _mockReflectHelper - .Setup(rh => rh.IsNonDerivedAttributeDefined(grandparentCleanupMethod, false)) + .Setup(rh => rh.IsAttributeDefined(grandparentCleanupMethod, false)) .Returns(true); _mockReflectHelper - .Setup(rh => rh.GetFirstDerivedAttributeOrDefault(grandparentCleanupMethod, true)) + .Setup(rh => rh.GetFirstAttributeOrDefault(grandparentCleanupMethod, true)) .Returns(new ClassCleanupAttribute(InheritanceBehavior.BeforeEachDerivedClass)); // Setup parent class init/cleanup methods _mockReflectHelper - .Setup(rh => rh.IsNonDerivedAttributeDefined(parentInitMethod, false)) + .Setup(rh => rh.IsAttributeDefined(parentInitMethod, false)) .Returns(true); _mockReflectHelper - .Setup(rh => rh.GetFirstDerivedAttributeOrDefault(parentInitMethod, true)) + .Setup(rh => rh.GetFirstAttributeOrDefault(parentInitMethod, true)) .Returns(new ClassInitializeAttribute(InheritanceBehavior.BeforeEachDerivedClass)); _mockReflectHelper - .Setup(rh => rh.IsNonDerivedAttributeDefined(parentCleanupMethod, false)) + .Setup(rh => rh.IsAttributeDefined(parentCleanupMethod, false)) .Returns(true); _mockReflectHelper - .Setup(rh => rh.GetFirstDerivedAttributeOrDefault(parentCleanupMethod, true)) + .Setup(rh => rh.GetFirstAttributeOrDefault(parentCleanupMethod, true)) .Returns(new ClassCleanupAttribute(InheritanceBehavior.BeforeEachDerivedClass)); var testMethod = new TestMethod("TestMethod", type.FullName!, "A", isAsync: false); @@ -597,10 +597,10 @@ public void GetTestMethodInfoShouldThrowIfClassInitHasIncorrectSignature() var testMethod = new TestMethod("M", type.FullName!, "A", isAsync: false); _mockReflectHelper.Setup( - rh => rh.IsDerivedAttributeDefined(type, false)).Returns(true); + rh => rh.IsAttributeDefined(type, false)).Returns(true); _mockReflectHelper.Setup( - rh => rh.IsNonDerivedAttributeDefined(type.GetMethod("AssemblyInit")!, false)).Returns(true); + rh => rh.IsAttributeDefined(type.GetMethod("AssemblyInit")!, false)).Returns(true); void A() => _typeCache.GetTestMethodInfo( @@ -626,10 +626,10 @@ public void GetTestMethodInfoShouldThrowIfClassCleanupHasIncorrectSignature() var testMethod = new TestMethod("M", type.FullName!, "A", isAsync: false); _mockReflectHelper.Setup( - rh => rh.IsDerivedAttributeDefined(type, false)).Returns(true); + rh => rh.IsAttributeDefined(type, false)).Returns(true); _mockReflectHelper.Setup( - rh => rh.IsNonDerivedAttributeDefined(type.GetMethod("AssemblyCleanup")!, false)).Returns(true); + rh => rh.IsAttributeDefined(type.GetMethod("AssemblyCleanup")!, false)).Returns(true); void A() => _typeCache.GetTestMethodInfo( @@ -655,10 +655,10 @@ public void GetTestMethodInfoShouldCacheTestInitializeAttribute() var testMethod = new TestMethod("TestInit", type.FullName!, "A", isAsync: false); _mockReflectHelper.Setup( - rh => rh.IsNonDerivedAttributeDefined(type, true)).Returns(true); + rh => rh.IsAttributeDefined(type, true)).Returns(true); _mockReflectHelper.Setup( - rh => rh.IsNonDerivedAttributeDefined(type.GetMethod("TestInit")!, false)).Returns(true); + rh => rh.IsAttributeDefined(type.GetMethod("TestInit")!, false)).Returns(true); _typeCache.GetTestMethodInfo( testMethod, @@ -674,10 +674,10 @@ public void GetTestMethodInfoShouldCacheTestCleanupAttribute() var testMethod = new TestMethod("TestCleanup", type.FullName!, "A", isAsync: false); _mockReflectHelper.Setup( - rh => rh.IsNonDerivedAttributeDefined(type, true)).Returns(true); + rh => rh.IsAttributeDefined(type, true)).Returns(true); _mockReflectHelper.Setup( - rh => rh.IsNonDerivedAttributeDefined(type.GetMethod("TestCleanup")!, false)).Returns(true); + rh => rh.IsAttributeDefined(type.GetMethod("TestCleanup")!, false)).Returns(true); _typeCache.GetTestMethodInfo( testMethod, @@ -693,10 +693,10 @@ public void GetTestMethodInfoShouldThrowIfTestInitOrCleanupHasIncorrectSignature var testMethod = new TestMethod("M", type.FullName!, "A", isAsync: false); _mockReflectHelper.Setup( - rh => rh.IsDerivedAttributeDefined(type, false)).Returns(true); + rh => rh.IsAttributeDefined(type, false)).Returns(true); _mockReflectHelper.Setup( - rh => rh.IsNonDerivedAttributeDefined(type.GetMethod("TestInit")!, false)).Returns(true); + rh => rh.IsAttributeDefined(type.GetMethod("TestInit")!, false)).Returns(true); void A() => _typeCache.GetTestMethodInfo( @@ -723,10 +723,10 @@ public void GetTestMethodInfoShouldCacheTestInitializeAttributeDefinedInBaseClas var testMethod = new TestMethod("TestMethod", type.FullName!, "A", isAsync: false); _mockReflectHelper.Setup( - rh => rh.IsNonDerivedAttributeDefined(type, true)).Returns(true); + rh => rh.IsAttributeDefined(type, true)).Returns(true); _mockReflectHelper.Setup( - rh => rh.IsNonDerivedAttributeDefined(baseType.GetMethod("TestInit")!, false)).Returns(true); + rh => rh.IsAttributeDefined(baseType.GetMethod("TestInit")!, false)).Returns(true); _typeCache.GetTestMethodInfo( testMethod, @@ -743,10 +743,10 @@ public void GetTestMethodInfoShouldCacheTestCleanupAttributeDefinedInBaseClass() var testMethod = new TestMethod("TestMethod", type.FullName!, "A", isAsync: false); _mockReflectHelper.Setup( - rh => rh.IsNonDerivedAttributeDefined(type, true)).Returns(true); + rh => rh.IsAttributeDefined(type, true)).Returns(true); _mockReflectHelper.Setup( - rh => rh.IsNonDerivedAttributeDefined(baseType.GetMethod("TestCleanup")!, false)).Returns(true); + rh => rh.IsAttributeDefined(baseType.GetMethod("TestCleanup")!, false)).Returns(true); _typeCache.GetTestMethodInfo( testMethod, @@ -763,7 +763,7 @@ public void GetTestMethodInfoShouldCacheClassInfoInstanceAndReuseFromCache() var testMethod = new TestMethod(methodInfo.Name, type.FullName!, "A", isAsync: false); _mockReflectHelper.Setup( - rh => rh.IsNonDerivedAttributeDefined(type, true)).Returns(true); + rh => rh.IsAttributeDefined(type, true)).Returns(true); _typeCache.GetTestMethodInfo( testMethod, @@ -809,12 +809,12 @@ 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(); + _mockReflectHelper.Setup(rh => rh.GetFirstAttributeOrDefault(It.IsAny(), false)).CallBase(); TestMethodInfo? testMethodInfo = _typeCache.GetTestMethodInfo( testMethod, new TestContextImplementation(testMethod, new ThreadSafeStringWriter(null!, "test"), new Dictionary())); - Verify(methodInfo == testMethodInfo!.TestMethod); + Verify(methodInfo == testMethodInfo!.MethodInfo); Verify(testMethodInfo.TimeoutInfo.Timeout == 0); Verify(_typeCache.ClassInfoCache.First() == testMethodInfo.Parent); Verify(testMethodInfo.Executor is not null); @@ -826,15 +826,15 @@ public void GetTestMethodInfoShouldReturnTestMethodInfoWithTimeout() MethodInfo methodInfo = type.GetMethod("TestMethodWithTimeout")!; var testMethod = new TestMethod(methodInfo.Name, type.FullName!, "A", isAsync: false); - _mockReflectHelper.Setup(rh => rh.IsNonDerivedAttributeDefined(methodInfo, false)) + _mockReflectHelper.Setup(rh => rh.IsAttributeDefined(methodInfo, false)) .Returns(true); - _mockReflectHelper.Setup(rh => rh.GetFirstDerivedAttributeOrDefault(It.IsAny(), false)).CallBase(); - _mockReflectHelper.Setup(rh => rh.GetFirstNonDerivedAttributeOrDefault(methodInfo, false)).CallBase(); + _mockReflectHelper.Setup(rh => rh.GetFirstAttributeOrDefault(It.IsAny(), false)).CallBase(); + _mockReflectHelper.Setup(rh => rh.GetFirstAttributeOrDefault(methodInfo, false)).CallBase(); TestMethodInfo? testMethodInfo = _typeCache.GetTestMethodInfo( testMethod, new TestContextImplementation(testMethod, new ThreadSafeStringWriter(null!, "test"), new Dictionary())); - Verify(methodInfo == testMethodInfo!.TestMethod); + Verify(methodInfo == testMethodInfo!.MethodInfo); Verify(testMethodInfo.TimeoutInfo.Timeout == 10); Verify(_typeCache.ClassInfoCache.First() == testMethodInfo.Parent); Verify(testMethodInfo.Executor is not null); @@ -846,9 +846,9 @@ public void GetTestMethodInfoShouldThrowWhenTimeoutIsNegative() MethodInfo methodInfo = type.GetMethod("TestMethodWithNegativeTimeout")!; var testMethod = new TestMethod(methodInfo.Name, type.FullName!, "A", isAsync: false); - _mockReflectHelper.Setup(rh => rh.IsNonDerivedAttributeDefined(methodInfo, false)) + _mockReflectHelper.Setup(rh => rh.IsAttributeDefined(methodInfo, false)) .Returns(true); - _mockReflectHelper.Setup(ReflectHelper => ReflectHelper.GetFirstNonDerivedAttributeOrDefault(methodInfo, false)) + _mockReflectHelper.Setup(ReflectHelper => ReflectHelper.GetFirstAttributeOrDefault(methodInfo, false)) .CallBase(); void A() => _typeCache.GetTestMethodInfo( @@ -873,9 +873,9 @@ public void GetTestMethodInfoShouldThrowWhenTimeoutIsZero() MethodInfo methodInfo = type.GetMethod("TestMethodWithTimeoutOfZero")!; var testMethod = new TestMethod(methodInfo.Name, type.FullName!, "A", isAsync: false); - _mockReflectHelper.Setup(rh => rh.IsNonDerivedAttributeDefined(methodInfo, false)) + _mockReflectHelper.Setup(rh => rh.IsAttributeDefined(methodInfo, false)) .Returns(true); - _mockReflectHelper.Setup(ReflectHelper => ReflectHelper.GetFirstNonDerivedAttributeOrDefault(methodInfo, false)) + _mockReflectHelper.Setup(ReflectHelper => ReflectHelper.GetFirstAttributeOrDefault(methodInfo, false)) .CallBase(); void A() => _typeCache.GetTestMethodInfo( @@ -935,9 +935,9 @@ public void GetTestMethodInfoWhenTimeoutAttributeSetShouldReturnTimeoutBasedOnAt MethodInfo methodInfo = type.GetMethod("TestMethodWithTimeout")!; var testMethod = new TestMethod(methodInfo.Name, type.FullName!, "A", isAsync: false); - _mockReflectHelper.Setup(rh => rh.IsNonDerivedAttributeDefined(methodInfo, false)) + _mockReflectHelper.Setup(rh => rh.IsAttributeDefined(methodInfo, false)) .Returns(true); - _mockReflectHelper.Setup(ReflectHelper => ReflectHelper.GetFirstNonDerivedAttributeOrDefault(methodInfo, false)) + _mockReflectHelper.Setup(ReflectHelper => ReflectHelper.GetFirstAttributeOrDefault(methodInfo, false)) .CallBase(); TestMethodInfo? testMethodInfo = _typeCache.GetTestMethodInfo( @@ -977,12 +977,12 @@ 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(); + _mockReflectHelper.Setup(rh => rh.GetFirstAttributeOrDefault(It.IsAny(), false)).CallBase(); TestMethodInfo? testMethodInfo = _typeCache.GetTestMethodInfo( testMethod, new TestContextImplementation(testMethod, new ThreadSafeStringWriter(null!, "test"), new Dictionary())); - Verify(methodInfo == testMethodInfo!.TestMethod); + Verify(methodInfo == testMethodInfo!.MethodInfo); Verify(testMethodInfo.TimeoutInfo.Timeout == 0); Verify(_typeCache.ClassInfoCache.First() == testMethodInfo.Parent); Verify(testMethodInfo.Executor is not null); @@ -1015,6 +1015,30 @@ public void GetTestMethodInfoShouldReportWarningIfCustomPropertyHasSameNameAsPre // Setting up the mock feels unnecessary when the original production implementation can work just fine. var typeCache = new TypeCache(new ReflectHelper()); Type type = typeof(DummyTestClassWithTestMethods); + MethodInfo methodInfo = type.GetMethod("TestMethodWithTestCategoryAsCustomProperty")!; + var testMethod = new TestMethod(methodInfo.Name, type.FullName!, "A", isAsync: false); + var testContext = new TestContextImplementation( + testMethod, + new ThreadSafeStringWriter(null!, "test"), + new Dictionary()); + + TestMethodInfo? testMethodInfo = typeCache.GetTestMethodInfo(testMethod, testContext); + + Verify(testMethodInfo is not null); + string expectedMessage = string.Format( + CultureInfo.InvariantCulture, + "UTA023: {0}: Cannot define predefined property {2} on method {1}.", + methodInfo.DeclaringType!.FullName!, + methodInfo.Name, + "TestCategory"); + Verify(expectedMessage == testMethodInfo.NotRunnableReason); + } + + public void GetTestMethodInfoShouldReportWarningIfCustomOwnerPropertyIsDefined() + { + // Test that [TestProperty("Owner", "value")] is still blocked + var typeCache = new TypeCache(new ReflectHelper()); + Type type = typeof(DummyTestClassWithTestMethods); MethodInfo methodInfo = type.GetMethod("TestMethodWithOwnerAsCustomProperty")!; var testMethod = new TestMethod(methodInfo.Name, type.FullName!, "A", isAsync: false); var testContext = new TestContextImplementation( @@ -1034,6 +1058,74 @@ public void GetTestMethodInfoShouldReportWarningIfCustomPropertyHasSameNameAsPre Verify(expectedMessage == testMethodInfo.NotRunnableReason); } + public void GetTestMethodInfoShouldReportWarningIfCustomPriorityPropertyIsDefined() + { + // Test that [TestProperty("Priority", "value")] is still blocked + var typeCache = new TypeCache(new ReflectHelper()); + Type type = typeof(DummyTestClassWithTestMethods); + MethodInfo methodInfo = type.GetMethod("TestMethodWithPriorityAsCustomProperty")!; + var testMethod = new TestMethod(methodInfo.Name, type.FullName!, "A", isAsync: false); + var testContext = new TestContextImplementation( + testMethod, + new ThreadSafeStringWriter(null!, "test"), + new Dictionary()); + + TestMethodInfo? testMethodInfo = typeCache.GetTestMethodInfo(testMethod, testContext); + + Verify(testMethodInfo is not null); + string expectedMessage = string.Format( + CultureInfo.InvariantCulture, + "UTA023: {0}: Cannot define predefined property {2} on method {1}.", + methodInfo.DeclaringType!.FullName!, + methodInfo.Name, + "Priority"); + Verify(expectedMessage == testMethodInfo.NotRunnableReason); + } + + public void GetTestMethodInfoShouldAllowActualOwnerAttribute() + { + // Test that the actual OwnerAttribute is allowed + var typeCache = new TypeCache(new ReflectHelper()); + Type type = typeof(DummyTestClassWithTestMethods); + MethodInfo methodInfo = type.GetMethod("TestMethodWithActualOwnerAttribute")!; + var testMethod = new TestMethod(methodInfo.Name, type.FullName!, "A", isAsync: false); + var testContext = new TestContextImplementation( + testMethod, + new ThreadSafeStringWriter(null!, "test"), + new Dictionary()); + + TestMethodInfo? testMethodInfo = typeCache.GetTestMethodInfo(testMethod, testContext); + + Verify(testMethodInfo is not null); + // Owner should be allowed - no NotRunnableReason should be set + Verify(string.IsNullOrEmpty(testMethodInfo.NotRunnableReason)); + // The Owner property should be added to the test context + Verify(testContext.TryGetPropertyValue("Owner", out object? ownerValue)); + Verify(ownerValue?.ToString() == "TestOwner"); + } + + public void GetTestMethodInfoShouldAllowActualPriorityAttribute() + { + // Test that the actual PriorityAttribute is allowed + var typeCache = new TypeCache(new ReflectHelper()); + Type type = typeof(DummyTestClassWithTestMethods); + MethodInfo methodInfo = type.GetMethod("TestMethodWithActualPriorityAttribute")!; + var testMethod = new TestMethod(methodInfo.Name, type.FullName!, "A", isAsync: false); + var testContext = new TestContextImplementation( + testMethod, + new ThreadSafeStringWriter(null!, "test"), + new Dictionary()); + + TestMethodInfo? testMethodInfo = typeCache.GetTestMethodInfo(testMethod, testContext); + + Verify(testMethodInfo is not null); + // Priority should be allowed - no NotRunnableReason should be set + Verify(string.IsNullOrEmpty(testMethodInfo.NotRunnableReason)); + // The Priority property should be added to the test context + Verify(testContext.TryGetPropertyValue("Priority", out object? priorityValue)); + Verify(priorityValue?.ToString() == "1"); + } + public void GetTestMethodInfoShouldReportWarningIfCustomPropertyNameIsEmpty() { // Not using _typeCache here which uses a mocked ReflectHelper which doesn't work well with this test. @@ -1110,12 +1202,12 @@ public void GetTestMethodInfoShouldReturnTestMethodInfoForDerivedTestClasses() 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(); + _mockReflectHelper.Setup(rh => rh.GetFirstAttributeOrDefault(It.IsAny(), false)).CallBase(); TestMethodInfo? testMethodInfo = _typeCache.GetTestMethodInfo( testMethod, new TestContextImplementation(testMethod, new ThreadSafeStringWriter(null!, "test"), new Dictionary())); - Verify(methodInfo == testMethodInfo!.TestMethod); + Verify(methodInfo == testMethodInfo!.MethodInfo); Verify(testMethodInfo.TimeoutInfo.Timeout == 0); Verify(_typeCache.ClassInfoCache.First() == testMethodInfo.Parent); Verify(testMethodInfo.Executor is not null); @@ -1127,12 +1219,12 @@ public void GetTestMethodInfoShouldReturnTestMethodInfoForDerivedClassMethodOver 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(); + _mockReflectHelper.Setup(rh => rh.GetFirstAttributeOrDefault(It.IsAny(), false)).CallBase(); TestMethodInfo? testMethodInfo = _typeCache.GetTestMethodInfo( testMethod, new TestContextImplementation(testMethod, new ThreadSafeStringWriter(null!, "test"), new Dictionary())); - Verify(methodInfo == testMethodInfo!.TestMethod); + Verify(methodInfo == testMethodInfo!.MethodInfo); Verify(testMethodInfo.TimeoutInfo.Timeout == 0); Verify(_typeCache.ClassInfoCache.First() == testMethodInfo.Parent); Verify(testMethodInfo.Executor is not null); @@ -1148,14 +1240,14 @@ public void GetTestMethodInfoShouldReturnTestMethodInfoForDeclaringTypeMethodOve DeclaringClassFullName = baseType.FullName!, }; - _mockReflectHelper.Setup(rh => rh.GetFirstDerivedAttributeOrDefault(It.IsAny(), false)).CallBase(); + _mockReflectHelper.Setup(rh => rh.GetFirstAttributeOrDefault(It.IsAny(), false)).CallBase(); TestMethodInfo? testMethodInfo = _typeCache.GetTestMethodInfo( testMethod, new TestContextImplementation(testMethod, new ThreadSafeStringWriter(null!, "test"), new Dictionary())); // The two MethodInfo instances will have different ReflectedType properties, // so cannot be compared directly. Use MethodHandle to verify it's the same. - Verify(methodInfo.MethodHandle == testMethodInfo!.TestMethod.MethodHandle); + Verify(methodInfo.MethodHandle == testMethodInfo!.MethodInfo.MethodHandle); Verify(testMethodInfo.TimeoutInfo.Timeout == 0); Verify(_typeCache.ClassInfoCache.First() == testMethodInfo.Parent); Verify(testMethodInfo.Executor is not null); @@ -1181,10 +1273,10 @@ public void ClassInfoListWithExecutableCleanupMethodsShouldReturnEmptyListWhenCl var testMethod = new TestMethod(methodInfo.Name, type.FullName!, "A", isAsync: false); _mockReflectHelper.Setup( - rh => rh.IsNonDerivedAttributeDefined(type, true)).Returns(true); + rh => rh.IsAttributeDefined(type, true)).Returns(true); _mockReflectHelper.Setup( - rh => rh.IsNonDerivedAttributeDefined(type.GetMethod("TestCleanup")!, false)).Returns(false); + rh => rh.IsAttributeDefined(type.GetMethod("TestCleanup")!, false)).Returns(false); _typeCache.GetTestMethodInfo( testMethod, @@ -1202,10 +1294,10 @@ public void ClassInfoListWithExecutableCleanupMethodsShouldReturnClassInfosWithE var testMethod = new TestMethod(methodInfo.Name, type.FullName!, "A", isAsync: false); _mockReflectHelper.Setup( - rh => rh.IsNonDerivedAttributeDefined(type, true)).Returns(true); + rh => rh.IsAttributeDefined(type, true)).Returns(true); _mockReflectHelper.Setup( - rh => rh.IsNonDerivedAttributeDefined(type.GetMethod("AssemblyCleanup")!, false)).Returns(true); + rh => rh.IsAttributeDefined(type.GetMethod("AssemblyCleanup")!, false)).Returns(true); _typeCache.GetTestMethodInfo( testMethod, @@ -1235,10 +1327,10 @@ public void AssemblyInfoListWithExecutableCleanupMethodsShouldReturnEmptyListWhe var testMethod = new TestMethod(methodInfo.Name, type.FullName!, "A", isAsync: false); _mockReflectHelper.Setup( - rh => rh.IsDerivedAttributeDefined(type, false)).Returns(true); + rh => rh.IsAttributeDefined(type, false)).Returns(true); _mockReflectHelper.Setup( - rh => rh.IsNonDerivedAttributeDefined(type.GetMethod("AssemblyCleanup")!, false)).Returns(false); + rh => rh.IsAttributeDefined(type.GetMethod("AssemblyCleanup")!, false)).Returns(false); _typeCache.GetTestMethodInfo( testMethod, @@ -1256,10 +1348,10 @@ public void AssemblyInfoListWithExecutableCleanupMethodsShouldReturnAssemblyInfo var testMethod = new TestMethod(methodInfo.Name, type.FullName!, "A", isAsync: false); _mockReflectHelper.Setup( - rh => rh.IsDerivedAttributeDefined(type, false)).Returns(true); + rh => rh.IsAttributeDefined(type, false)).Returns(true); _mockReflectHelper.Setup( - rh => rh.IsNonDerivedAttributeDefined(type.GetMethod("AssemblyCleanup")!, false)).Returns(true); + rh => rh.IsAttributeDefined(type.GetMethod("AssemblyCleanup")!, false)).Returns(true); _typeCache.GetTestMethodInfo( testMethod, @@ -1281,7 +1373,7 @@ public void ResolveExpectedExceptionHelperShouldThrowIfMultipleExpectedException MethodInfo methodInfo = type.GetMethod("TestMethodWithMultipleExpectedException")!; var testMethod = new TestMethod(methodInfo.Name, type.FullName!, "A", isAsync: false); - _mockReflectHelper.Setup(rh => rh.IsNonDerivedAttributeDefined(methodInfo, false)) + _mockReflectHelper.Setup(rh => rh.IsAttributeDefined(methodInfo, false)) .Returns(true); try @@ -1350,6 +1442,30 @@ public void TestMethodWithOwnerAsCustomProperty() { } + [TestMethod] + [TestProperty("TestCategory", "SomeCategory")] + public void TestMethodWithTestCategoryAsCustomProperty() + { + } + + [TestMethod] + [Owner("TestOwner")] + public void TestMethodWithActualOwnerAttribute() + { + } + + [TestMethod] + [Priority(1)] + public void TestMethodWithActualPriorityAttribute() + { + } + + [TestMethod] + [TestProperty("Priority", "2")] + public void TestMethodWithPriorityAsCustomProperty() + { + } + [TestMethod] [TestProperty("", "You")] public void TestMethodWithEmptyCustomPropertyName() diff --git a/test/UnitTests/MSTestAdapter.UnitTests/Execution/UnitTestResultTest.cs b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Execution/UnitTestResultTest.cs similarity index 100% rename from test/UnitTests/MSTestAdapter.UnitTests/Execution/UnitTestResultTest.cs rename to test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Execution/UnitTestResultTest.cs diff --git a/test/UnitTests/MSTestAdapter.UnitTests/Execution/UnitTestRunnerTests.cs b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Execution/UnitTestRunnerTests.cs similarity index 98% rename from test/UnitTests/MSTestAdapter.UnitTests/Execution/UnitTestRunnerTests.cs rename to test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Execution/UnitTestRunnerTests.cs index 380c69c3fd..fec3b90852 100644 --- a/test/UnitTests/MSTestAdapter.UnitTests/Execution/UnitTestRunnerTests.cs +++ b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Execution/UnitTestRunnerTests.cs @@ -31,7 +31,7 @@ public UnitTestRunnerTests() _mockMessageLogger = new Mock(); PlatformServiceProvider.Instance = _testablePlatformServiceProvider; - _unitTestRunner = new UnitTestRunner(GetSettingsWithDebugTrace(false)!, Array.Empty(), null); + _unitTestRunner = new UnitTestRunner(GetSettingsWithDebugTrace(false)!, [], null); } protected override void Dispose(bool disposing) @@ -65,7 +65,7 @@ public void ConstructorShouldPopulateSettings() }); MSTestSettings adapterSettings = MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsName, _mockMessageLogger.Object)!; - var assemblyEnumerator = new UnitTestRunner(adapterSettings, Array.Empty(), null); + var assemblyEnumerator = new UnitTestRunner(adapterSettings, [], null); Verify(MSTestSettings.CurrentSettings.ForcedLegacyMode); Verify(MSTestSettings.CurrentSettings.TestSettingsFile == "DummyPath\\TestSettings1.testsettings"); @@ -283,7 +283,7 @@ public async Task RunSingleTestShouldSetTestsAsInProgressInTestContext() public async Task RunSingleTestShouldCallAssemblyInitializeAndClassInitializeMethodsInOrder() { var mockReflectHelper = new Mock(); - _unitTestRunner = new UnitTestRunner(new MSTestSettings(), Array.Empty(), null, mockReflectHelper.Object); + _unitTestRunner = new UnitTestRunner(new MSTestSettings(), [], null, mockReflectHelper.Object); Type type = typeof(DummyTestClassWithInitializeMethods); MethodInfo methodInfo = type.GetMethod("TestMethod")!; @@ -292,7 +292,7 @@ public async Task RunSingleTestShouldCallAssemblyInitializeAndClassInitializeMet _testablePlatformServiceProvider.MockFileOperations.Setup(fo => fo.LoadAssembly("A", It.IsAny())) .Returns(Assembly.GetExecutingAssembly()); mockReflectHelper.Setup( - rh => rh.IsNonDerivedAttributeDefined(type.GetMethod("AssemblyInitialize")!, It.IsAny())) + rh => rh.IsAttributeDefined(type.GetMethod("AssemblyInitialize")!, It.IsAny())) .Returns(true); int validator = 1; diff --git a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Extensions/ExceptionExtensionsTests.cs b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Extensions/ExceptionExtensionsTests.cs index 4aba233eba..bf28034646 100644 --- a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Extensions/ExceptionExtensionsTests.cs +++ b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Extensions/ExceptionExtensionsTests.cs @@ -1,10 +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. +using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel; using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Extensions; +using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Resources; using TestFramework.ForTestingMSTest; +using UTF = Microsoft.VisualStudio.TestTools.UnitTesting; + namespace MSTestAdapter.PlatformServices.Tests.Extensions; public class ExceptionExtensionsTests : TestContainer @@ -27,4 +31,196 @@ public void GetExceptionMessageShouldReturnInnerExceptionMessageAsWell() Verify(expectedMessage == ex.GetExceptionMessage()); } + + #region TryGetExceptionMessage scenarios + + public void ExceptionTryGetMessageGetsTheExceptionMessage() + { + var exception = new Exception("dummyMessage"); + + Verify(exception.TryGetMessage() == "dummyMessage"); + } + + public void ExceptionTryGetMessageReturnsEmptyStringIfExceptionMessageIsNull() + { + var exception = new DummyException(() => null!); + + Verify(exception.TryGetMessage() == string.Empty); + } + + public void ExceptionTryGetMessageReturnsErrorMessageIfExceptionIsNull() + { + string errorMessage = string.Format(CultureInfo.InvariantCulture, Resource.UTF_FailedToGetExceptionMessage, "null"); + + var exception = (Exception?)null; + + Verify(errorMessage == exception.TryGetMessage()); + } + + public void ExceptionTryGetMessageShouldThrowIfExceptionMessageThrows() + { + var exception = new DummyException(() => throw new NotImplementedException()); + + VerifyThrows(() => exception.TryGetMessage()); + } + + #endregion + + #region TryGetStackTraceInformation scenarios + + public void TryGetStackTraceInformationReturnsNullIfExceptionStackTraceIsNullOrEmpty() + { + var exception = new DummyExceptionForStackTrace(() => null!); + + Verify(exception.TryGetStackTraceInformation() is null); + } + + public void TryGetStackTraceInformationReturnsStackTraceForAnException() + { + var exception = new DummyExceptionForStackTrace(() => " at A()\r\n at B()"); + + StackTraceInformation? stackTraceInformation = exception.TryGetStackTraceInformation(); + + Verify(stackTraceInformation!.ErrorStackTrace.StartsWith(" at A()", StringComparison.Ordinal)); + Verify(stackTraceInformation.ErrorFilePath is null); + Verify(stackTraceInformation.ErrorLineNumber == 0); + } + + public void TryGetStackTraceInformationShouldThrowIfStackTraceThrows() + { + var exception = new DummyExceptionForStackTrace(() => throw new NotImplementedException()); + + VerifyThrows(() => exception.TryGetStackTraceInformation()); + } + +#pragma warning disable CA1710 // Identifiers should have correct suffix + public class DummyExceptionForStackTrace : Exception +#pragma warning restore CA1710 // Identifiers should have correct suffix + { + private readonly Func _getStackTrace; + + public DummyExceptionForStackTrace(Func getStackTrace) => _getStackTrace = getStackTrace; + + public override string StackTrace => _getStackTrace(); + } + + internal class DummyException : Exception + { + private readonly Func _getMessage; + + public DummyException(Func message) => _getMessage = message; + + public override string Message => _getMessage(); + } + + #endregion + + #region IsUnitTestAssertException scenarios + + public void IsUnitTestAssertExceptionReturnsTrueIfExceptionIsAssertException() + { + var exception = new AssertInconclusiveException(); + Verify(exception.TryGetUnitTestAssertException(out _, out _, out _)); + } + + public void IsUnitTestAssertExceptionReturnsFalseIfExceptionIsNotAssertException() + { + var exception = new NotImplementedException(); + Verify(!exception.TryGetUnitTestAssertException(out _, out _, out _)); + } + + public void IsUnitTestAssertExceptionSetsOutcomeAsInconclusiveIfAssertInconclusiveException() + { + var exception = new AssertInconclusiveException("Dummy Message", new NotImplementedException("notImplementedException")); + exception.TryGetUnitTestAssertException(out UTF.UnitTestOutcome outcome, out string? exceptionMessage, out _); + + Verify(outcome == UTF.UnitTestOutcome.Inconclusive); + Verify(exceptionMessage == "Dummy Message"); + } + + public void IsUnitTestAssertExceptionSetsOutcomeAsFailedIfAssertFailedException() + { + var exception = new AssertFailedException("Dummy Message", new NotImplementedException("notImplementedException")); + exception.TryGetUnitTestAssertException(out UTF.UnitTestOutcome outcome, out string? exceptionMessage, out _); + + Verify(outcome == UTF.UnitTestOutcome.Failed); + Verify(exceptionMessage == "Dummy Message"); + } + #endregion + + #region GetRealException scenarios + public void GetRealExceptionGetsTheTopExceptionWhenThereIsJustOne() + { + var exception = new InvalidOperationException(); + Exception actual = exception.GetRealException(); + + Verify(actual is InvalidOperationException); + } + + public void GetRealExceptionGetsTheInnerExceptionWhenTheExceptionIsTargetInvocation() + { + var exception = new TargetInvocationException(new InvalidOperationException()); + Exception actual = exception.GetRealException(); + + Verify(actual is InvalidOperationException); + } + + public void GetRealExceptionGetsTheTargetInvocationExceptionWhenTargetInvocationIsProvidedWithNullInnerException() + { + var exception = new TargetInvocationException(null); + Exception actual = exception.GetRealException(); + + Verify(actual is TargetInvocationException); + } + + public void GetRealExceptionGetsTheInnerMostRealException() + { + var exception = new TargetInvocationException(new TargetInvocationException(new TargetInvocationException(new InvalidOperationException()))); + Exception actual = exception.GetRealException(); + + Verify(actual is InvalidOperationException); + } + + public void GetRealExceptionGetsTheInnerMostTargetInvocationException() + { + var exception = new TargetInvocationException(new TargetInvocationException(new TargetInvocationException("inner most", null))); + Exception actual = exception.GetRealException(); + + Verify(actual is TargetInvocationException); + Verify(actual.Message == "inner most"); + } + + public void GetRealExceptionGetsTheInnerExceptionWhenTheExceptionIsTypeInitialization() + { + var exception = new TypeInitializationException("some type", new InvalidOperationException()); + Exception actual = exception.GetRealException(); + + Verify(actual is InvalidOperationException); + } + + public void GetRealExceptionGetsTheTypeInitializationExceptionWhenTypeInitializationIsProvidedWithNullInnerException() + { + var exception = new TypeInitializationException("some type", null); + Exception actual = exception.GetRealException(); + + Verify(actual is TypeInitializationException); + } + + public void GetRealExceptionGetsTheInnerMostRealExceptionOfTypeInitialization() + { + var exception = new TypeInitializationException("some type", new TypeInitializationException("some type", new TypeInitializationException("some type", new InvalidOperationException()))); + Exception actual = exception.GetRealException(); + + Verify(actual is InvalidOperationException); + } + + public void GetRealExceptionGetsTheInnerMostTypeInitializationException() + { + var exception = new TypeInitializationException("some type", new TypeInitializationException("some type", new TypeInitializationException("inner most", null))); + Exception actual = exception.GetRealException(); + + Verify(actual is TypeInitializationException); + Verify(actual.Message == "The type initializer for 'inner most' threw an exception."); + } + #endregion } diff --git a/test/UnitTests/MSTestAdapter.UnitTests/Extensions/MethodInfoExtensionsTests.cs b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Extensions/MethodInfoExtensionsTests.cs similarity index 96% rename from test/UnitTests/MSTestAdapter.UnitTests/Extensions/MethodInfoExtensionsTests.cs rename to test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Extensions/MethodInfoExtensionsTests.cs index b8887cb5f2..d330dc42e8 100644 --- a/test/UnitTests/MSTestAdapter.UnitTests/Extensions/MethodInfoExtensionsTests.cs +++ b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Extensions/MethodInfoExtensionsTests.cs @@ -1,14 +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. -using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter; using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Extensions; using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel; +using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Extensions; +using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Resources; using TestFramework.ForTestingMSTest; using UTF = Microsoft.VisualStudio.TestTools.UnitTesting; -using UTFExtension = Microsoft.VisualStudio.TestTools.UnitTesting; namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.UnitTests.Extensions; @@ -244,13 +244,13 @@ public void HasCorrectTestMethodSignatureShouldReturnFalseForAsyncTestMethodsWit public void HasCorrectTimeoutShouldReturnFalseForMethodsWithInvalidTimeoutAttribute() { - var timeoutAttribute = new UTF.TimeoutAttribute(-11); + var timeoutAttribute = new TimeoutAttribute(-11); Verify(!timeoutAttribute.HasCorrectTimeout); } public void HasCorrectTimeoutShouldReturnTrueForMethodsWithTimeoutAttribute() { - var timeoutAttribute = new UTF.TimeoutAttribute(11); + var timeoutAttribute = new TimeoutAttribute(11); Verify(timeoutAttribute.HasCorrectTimeout); } @@ -462,18 +462,18 @@ public static void PublicStaticMethodWithInt(int a) { } - public static int PublicStaticMethodWithTCReturningInt(UTFExtension.TestContext tc) => 0; + public static int PublicStaticMethodWithTCReturningInt(TestContext tc) => 0; - public static void PublicStaticMethodWithTC(UTFExtension.TestContext tc) + public static void PublicStaticMethodWithTC(TestContext tc) { } - public static async Task PublicStaticAsyncTaskMethodWithTC(UTFExtension.TestContext tc) => await Task.FromResult(true).ConfigureAwait(false); + public static async Task PublicStaticAsyncTaskMethodWithTC(TestContext tc) => await Task.FromResult(true).ConfigureAwait(false); - public static Task PublicStaticNonAsyncTaskMethodWithTC(UTFExtension.TestContext tc) => Task.FromResult(true); + public static Task PublicStaticNonAsyncTaskMethodWithTC(TestContext tc) => Task.FromResult(true); [SuppressMessage("Usage", "VSTHRD100:Avoid async void methods", Justification = "Done on purpose")] - public static async void PublicStaticAsyncVoidMethodWithTC(UTFExtension.TestContext tc) => await Task.FromResult(true).ConfigureAwait(false); + public static async void PublicStaticAsyncVoidMethodWithTC(TestContext tc) => await Task.FromResult(true).ConfigureAwait(false); public static int PublicStaticMethodReturningInt() => 0; @@ -507,12 +507,12 @@ public void PublicMethodWithInt(int a) public Task PublicNonAsyncTaskMethod() => Task.FromResult(true); - [UTF.Timeout(-11)] + [Timeout(-11)] public void PublicMethodWithInvalidTimeout() { } - [UTF.Timeout(11)] + [Timeout(11)] public void PublicMethodWithTimeout() { } diff --git a/test/UnitTests/MSTestAdapter.UnitTests/Extensions/TestCaseExtensionsTests.cs b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Extensions/TestCaseExtensionsTests.cs similarity index 79% rename from test/UnitTests/MSTestAdapter.UnitTests/Extensions/TestCaseExtensionsTests.cs rename to test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Extensions/TestCaseExtensionsTests.cs index 5e2ba8b8df..1c8ad83b56 100644 --- a/test/UnitTests/MSTestAdapter.UnitTests/Extensions/TestCaseExtensionsTests.cs +++ b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Extensions/TestCaseExtensionsTests.cs @@ -2,12 +2,11 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Extensions; +using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices; using Microsoft.VisualStudio.TestPlatform.ObjectModel; using TestFramework.ForTestingMSTest; -using Constants = Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Constants; - namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.UnitTests.Extensions; public class TestCaseExtensionsTests : TestContainer @@ -20,9 +19,9 @@ public void ToUnitTestElementShouldReturnUnitTestElementWithFieldsSet() }; string[] testCategories = ["DummyCategory"]; - testCase.SetPropertyValue(Constants.PriorityProperty, 2); - testCase.SetPropertyValue(Constants.TestCategoryProperty, testCategories); - testCase.SetPropertyValue(Constants.TestClassNameProperty, "DummyClassName"); + testCase.SetPropertyValue(EngineConstants.PriorityProperty, 2); + testCase.SetPropertyValue(EngineConstants.TestCategoryProperty, testCategories); + testCase.SetPropertyValue(EngineConstants.TestClassNameProperty, "DummyClassName"); var resultUnitTestElement = testCase.ToUnitTestElement(testCase.Source); @@ -37,7 +36,7 @@ public void ToUnitTestElementShouldReturnUnitTestElementWithFieldsSet() public void ToUnitTestElementForTestCaseWithNoPropertiesShouldReturnUnitTestElementWithDefaultFields() { TestCase testCase = new("DummyClass.DummyMethod", new("DummyUri", UriKind.Relative), Assembly.GetCallingAssembly().FullName!); - testCase.SetPropertyValue(Constants.TestClassNameProperty, "DummyClassName"); + testCase.SetPropertyValue(EngineConstants.TestClassNameProperty, "DummyClassName"); var resultUnitTestElement = testCase.ToUnitTestElement(testCase.Source); @@ -49,8 +48,8 @@ public void ToUnitTestElementForTestCaseWithNoPropertiesShouldReturnUnitTestElem public void ToUnitTestElementShouldAddDeclaringClassNameToTestElementWhenAvailable() { TestCase testCase = new("DummyClass.DummyMethod", new("DummyUri", UriKind.Relative), Assembly.GetCallingAssembly().FullName!); - testCase.SetPropertyValue(Constants.TestClassNameProperty, "DummyClassName"); - testCase.SetPropertyValue(Constants.DeclaringClassNameProperty, "DummyDeclaringClassName"); + testCase.SetPropertyValue(EngineConstants.TestClassNameProperty, "DummyClassName"); + testCase.SetPropertyValue(EngineConstants.DeclaringClassNameProperty, "DummyDeclaringClassName"); var resultUnitTestElement = testCase.ToUnitTestElement(testCase.Source); diff --git a/test/UnitTests/MSTestAdapter.UnitTests/Extensions/TestContextExtensionsTests.cs b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Extensions/TestContextExtensionsTests.cs similarity index 100% rename from test/UnitTests/MSTestAdapter.UnitTests/Extensions/TestContextExtensionsTests.cs rename to test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Extensions/TestContextExtensionsTests.cs diff --git a/test/UnitTests/MSTestAdapter.UnitTests/Extensions/TestResultExtensionsTests.cs b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Extensions/TestResultExtensionsTests.cs similarity index 93% rename from test/UnitTests/MSTestAdapter.UnitTests/Extensions/TestResultExtensionsTests.cs rename to test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Extensions/TestResultExtensionsTests.cs index 0b116fc5a8..28f878600d 100644 --- a/test/UnitTests/MSTestAdapter.UnitTests/Extensions/TestResultExtensionsTests.cs +++ b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Extensions/TestResultExtensionsTests.cs @@ -2,6 +2,7 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Extensions; +using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices; using TestFramework.ForTestingMSTest; @@ -32,7 +33,7 @@ public void ToUnitTestResultsForTestResultWithExceptionConvertsToUnitTestResults public void ToUnitTestResultsForTestResultShouldSetLoggingDataForConvertedUnitTestResults() { var timespan = default(TimeSpan); - var result = new UTF.TestResult + var result = new TestResult { DebugTrace = "debugTrace", DisplayName = "displayName", @@ -43,7 +44,7 @@ public void ToUnitTestResultsForTestResultShouldSetLoggingDataForConvertedUnitTe }; var convertedResult = result.ToTestResult(new() { DisplayName = result.DisplayName }, default, default, string.Empty, new()); - VSTestTestResultMessage[] stdOutMessages = convertedResult.Messages.Where(m => m.Category == VSTestTestResultMessage.StandardOutCategory).ToArray(); + VSTestTestResultMessage[] stdOutMessages = [.. convertedResult.Messages.Where(m => m.Category == VSTestTestResultMessage.StandardOutCategory)]; Verify(stdOutMessages[0].Text == "logOutput"); Verify(convertedResult.Messages.Single(m => m.Category == VSTestTestResultMessage.StandardErrorCategory).Text == "logError"); Verify(convertedResult.DisplayName == "displayName (Data Row 1)"); @@ -166,9 +167,9 @@ public void ToUnitTestResultsForTestResultShouldSetParentInfo() var convertedResult = result.ToTestResult(new(), default, default, string.Empty, new()); - Verify(executionId == (Guid)convertedResult.GetPropertyValue(MSTest.TestAdapter.Constants.ExecutionIdProperty)!); - Verify(parentExecId == (Guid)convertedResult.GetPropertyValue(MSTest.TestAdapter.Constants.ParentExecIdProperty)!); - Verify(innerResultsCount == (int)convertedResult.GetPropertyValue(MSTest.TestAdapter.Constants.InnerResultsCountProperty)!); + Verify(executionId == (Guid)convertedResult.GetPropertyValue(EngineConstants.ExecutionIdProperty)!); + Verify(parentExecId == (Guid)convertedResult.GetPropertyValue(EngineConstants.ParentExecIdProperty)!); + Verify(innerResultsCount == (int)convertedResult.GetPropertyValue(EngineConstants.InnerResultsCountProperty)!); } public void ToUnitTestResultsShouldHaveResultsFileProvidedToTestResult() @@ -177,7 +178,7 @@ public void ToUnitTestResultsShouldHaveResultsFileProvidedToTestResult() // Otherwise, ToTestResult will crash because it calls new Uri on the result file. string resultFile = Path.GetFullPath("DummyFile.txt"); - var result = new TestResult { ResultFiles = new List() { resultFile } }; + var result = new TestResult { ResultFiles = [resultFile] }; var convertedResult = result.ToTestResult(new(), default, default, string.Empty, new()); Verify(convertedResult.Attachments[0].Attachments[0].Description == resultFile); } diff --git a/test/UnitTests/MSTestAdapter.UnitTests/Extensions/UnitTestOutcomeExtensionsTests.cs b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Extensions/UnitTestOutcomeExtensionsTests.cs similarity index 73% rename from test/UnitTests/MSTestAdapter.UnitTests/Extensions/UnitTestOutcomeExtensionsTests.cs rename to test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Extensions/UnitTestOutcomeExtensionsTests.cs index 77c056e763..86bc0fb6cb 100644 --- a/test/UnitTests/MSTestAdapter.UnitTests/Extensions/UnitTestOutcomeExtensionsTests.cs +++ b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Extensions/UnitTestOutcomeExtensionsTests.cs @@ -14,7 +14,7 @@ public class UnitTestOutcomeExtensionsTests : TestContainer { public void ToUnitTestOutComeForPassedTestResultsConvertsToPassedUnitTestOutCome() { - UTF.UnitTestOutcome frameworkOutcome = UTF.UnitTestOutcome.Passed; + UnitTestOutcome frameworkOutcome = UTF.UnitTestOutcome.Passed; var convertedOutcome = frameworkOutcome.ToUnitTestOutcome(); Verify(convertedOutcome == AdapterTestOutcome.Passed); @@ -22,7 +22,7 @@ public void ToUnitTestOutComeForPassedTestResultsConvertsToPassedUnitTestOutCome public void ToUnitTestResultsForFailedTestResultsConvertsToFailedUnitTestResults() { - UTF.UnitTestOutcome frameworkOutcome = UTF.UnitTestOutcome.Failed; + UnitTestOutcome frameworkOutcome = UTF.UnitTestOutcome.Failed; var convertedOutcome = frameworkOutcome.ToUnitTestOutcome(); Verify(convertedOutcome == AdapterTestOutcome.Failed); @@ -30,7 +30,7 @@ public void ToUnitTestResultsForFailedTestResultsConvertsToFailedUnitTestResults public void ToUnitTestResultsForInProgressTestResultsConvertsToInProgressUnitTestResults() { - UTF.UnitTestOutcome frameworkOutcome = UTF.UnitTestOutcome.InProgress; + UnitTestOutcome frameworkOutcome = UTF.UnitTestOutcome.InProgress; var convertedOutcome = frameworkOutcome.ToUnitTestOutcome(); Verify(convertedOutcome == AdapterTestOutcome.InProgress); @@ -38,7 +38,7 @@ public void ToUnitTestResultsForInProgressTestResultsConvertsToInProgressUnitTes public void ToUnitTestResultsForInconclusiveTestResultsConvertsToInconclusiveUnitTestResults() { - UTF.UnitTestOutcome frameworkOutcome = UTF.UnitTestOutcome.Inconclusive; + UnitTestOutcome frameworkOutcome = UTF.UnitTestOutcome.Inconclusive; var convertedOutcome = frameworkOutcome.ToUnitTestOutcome(); Verify(convertedOutcome == AdapterTestOutcome.Inconclusive); @@ -46,7 +46,7 @@ public void ToUnitTestResultsForInconclusiveTestResultsConvertsToInconclusiveUni public void ToUnitTestResultsForTimeoutTestResultsConvertsToTimeoutUnitTestResults() { - UTF.UnitTestOutcome frameworkOutcome = UTF.UnitTestOutcome.Timeout; + UnitTestOutcome frameworkOutcome = UTF.UnitTestOutcome.Timeout; var convertedOutcome = frameworkOutcome.ToUnitTestOutcome(); Verify(convertedOutcome == AdapterTestOutcome.Timeout); @@ -54,7 +54,7 @@ public void ToUnitTestResultsForTimeoutTestResultsConvertsToTimeoutUnitTestResul public void ToUnitTestResultsForUnknownTestResultsConvertsToErrorUnitTestResults() { - UTF.UnitTestOutcome frameworkOutcome = UTF.UnitTestOutcome.Unknown; + UnitTestOutcome frameworkOutcome = UTF.UnitTestOutcome.Unknown; var convertedOutcome = frameworkOutcome.ToUnitTestOutcome(); Verify(convertedOutcome == AdapterTestOutcome.Error); @@ -62,25 +62,25 @@ public void ToUnitTestResultsForUnknownTestResultsConvertsToErrorUnitTestResults public void GetMoreImportantOutcomeShouldReturnFailIfTwoOutcomesAreFailedAndInconclusive() { - UTF.UnitTestOutcome resultOutcome = UTF.UnitTestOutcome.Failed.GetMoreImportantOutcome(UTF.UnitTestOutcome.Inconclusive); + UnitTestOutcome resultOutcome = UTF.UnitTestOutcome.Failed.GetMoreImportantOutcome(UTF.UnitTestOutcome.Inconclusive); Verify(resultOutcome == UTF.UnitTestOutcome.Failed); } public void GetMoreImportantOutcomeShouldReturnInconclusiveIfTwoOutcomesArePassedAndInconclusive() { - UTF.UnitTestOutcome resultOutcome = UTF.UnitTestOutcome.Passed.GetMoreImportantOutcome(UTF.UnitTestOutcome.Inconclusive); + UnitTestOutcome resultOutcome = UTF.UnitTestOutcome.Passed.GetMoreImportantOutcome(UTF.UnitTestOutcome.Inconclusive); Verify(resultOutcome == UTF.UnitTestOutcome.Inconclusive); } public void GetMoreImportantOutcomeShouldReturnFailedIfTwoOutcomesArePassedAndFailed() { - UTF.UnitTestOutcome resultOutcome = UTF.UnitTestOutcome.Passed.GetMoreImportantOutcome(UTF.UnitTestOutcome.Failed); + UnitTestOutcome resultOutcome = UTF.UnitTestOutcome.Passed.GetMoreImportantOutcome(UTF.UnitTestOutcome.Failed); Verify(resultOutcome == UTF.UnitTestOutcome.Failed); } public void GetMoreImportantOutcomeShouldReturnFailedIfBothOutcomesAreFailed() { - UTF.UnitTestOutcome resultOutcome = UTF.UnitTestOutcome.Failed.GetMoreImportantOutcome(UTF.UnitTestOutcome.Failed); + UnitTestOutcome resultOutcome = UTF.UnitTestOutcome.Failed.GetMoreImportantOutcome(UTF.UnitTestOutcome.Failed); Verify(resultOutcome == UTF.UnitTestOutcome.Failed); } } diff --git a/test/UnitTests/MSTestAdapter.UnitTests/Helpers/DataSerializationHelperTests.cs b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Helpers/DataSerializationHelperTests.cs similarity index 73% rename from test/UnitTests/MSTestAdapter.UnitTests/Helpers/DataSerializationHelperTests.cs rename to test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Helpers/DataSerializationHelperTests.cs index 17f093237a..1b7e4aa18e 100644 --- a/test/UnitTests/MSTestAdapter.UnitTests/Helpers/DataSerializationHelperTests.cs +++ b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Helpers/DataSerializationHelperTests.cs @@ -55,4 +55,28 @@ public void DataSerializerShouldRoundTripDateTimeOfKindUtc() Verify(actual[0]!.Equals(source)); Verify(((DateTime)actual[0]!).Kind.Equals(source.Kind)); } + +#if NET7_0_OR_GREATER + public void DataSerializerShouldRoundTripDateOnly() + { + var source = new DateOnly(1999, 11, 3); + + object?[]? actual = DataSerializationHelper.Deserialize(DataSerializationHelper.Serialize([source])); + + Verify(actual!.Length == 1); + Verify(actual[0]!.GetType() == typeof(DateOnly)); + Verify(actual[0]!.Equals(source)); + } + + public void DataSerializerShouldRoundTripTimeOnly() + { + var source = new TimeOnly(hour: 14, minute: 50, second: 13, millisecond: 15); + + object?[]? actual = DataSerializationHelper.Deserialize(DataSerializationHelper.Serialize([source])); + + Verify(actual!.Length == 1); + Verify(actual[0]!.GetType() == typeof(TimeOnly)); + Verify(actual[0]!.Equals(source)); + } +#endif } diff --git a/test/UnitTests/MSTestAdapter.UnitTests/Helpers/DictionaryHelperTests.cs b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Helpers/DictionaryHelperTests.cs similarity index 100% rename from test/UnitTests/MSTestAdapter.UnitTests/Helpers/DictionaryHelperTests.cs rename to test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Helpers/DictionaryHelperTests.cs diff --git a/test/UnitTests/MSTestAdapter.UnitTests/Helpers/ReflectHelperTests.cs b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Helpers/ReflectHelperTests.cs similarity index 88% rename from test/UnitTests/MSTestAdapter.UnitTests/Helpers/ReflectHelperTests.cs rename to test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Helpers/ReflectHelperTests.cs index f6856b60ba..f77540b6f9 100644 --- a/test/UnitTests/MSTestAdapter.UnitTests/Helpers/ReflectHelperTests.cs +++ b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Helpers/ReflectHelperTests.cs @@ -49,7 +49,7 @@ public void GetTestCategoryAttributeShouldIncludeTestCategoriesAtClassLevel() _attributeMockingHelper.SetCustomAttribute(typeof(TestCategoryBaseAttribute), [new TestCategoryAttribute("ClassLevel")], MemberTypes.TypeInfo); string[] expected = ["ClassLevel"]; - string[] actual = _reflectHelper.GetTestCategories(_method.Object, typeof(ReflectHelperTests)).ToArray(); + string[] actual = [.. _reflectHelper.GetTestCategories(_method.Object, typeof(ReflectHelperTests))]; Verify(expected.SequenceEqual(actual)); } @@ -64,7 +64,7 @@ public void GetTestCategoryAttributeShouldIncludeTestCategoriesAtAllLevels() _attributeMockingHelper.SetCustomAttribute(typeof(TestCategoryBaseAttribute), [new TestCategoryAttribute("ClassLevel")], MemberTypes.TypeInfo); _attributeMockingHelper.SetCustomAttribute(typeof(TestCategoryBaseAttribute), [new TestCategoryAttribute("MethodLevel")], MemberTypes.Method); - string[] actual = _reflectHelper.GetTestCategories(_method.Object, typeof(ReflectHelperTests)).ToArray(); + string[] actual = [.. _reflectHelper.GetTestCategories(_method.Object, typeof(ReflectHelperTests))]; string[] expected = ["MethodLevel", "ClassLevel", "AsmLevel1", "AsmLevel2", "AsmLevel3"]; Verify(expected.SequenceEqual(actual)); @@ -82,7 +82,7 @@ public void GetTestCategoryAttributeShouldConcatCustomAttributeOfSameType() _attributeMockingHelper.SetCustomAttribute(typeof(TestCategoryBaseAttribute), [new TestCategoryAttribute("MethodLevel1")], MemberTypes.Method); _attributeMockingHelper.SetCustomAttribute(typeof(TestCategoryBaseAttribute), [new TestCategoryAttribute("MethodLevel2")], MemberTypes.Method); - string[] actual = _reflectHelper.GetTestCategories(_method.Object, typeof(ReflectHelperTests)).ToArray(); + string[] actual = [.. _reflectHelper.GetTestCategories(_method.Object, typeof(ReflectHelperTests))]; string[] expected = ["MethodLevel1", "MethodLevel2", "ClassLevel1", "ClassLevel2", "AsmLevel1", "AsmLevel2"]; Verify(expected.SequenceEqual(actual)); @@ -97,7 +97,7 @@ public void GetTestCategoryAttributeShouldIncludeTestCategoriesAtAssemblyLevel() string[] expected = ["AsmLevel"]; - string[] actual = _reflectHelper.GetTestCategories(_method.Object, typeof(ReflectHelperTests)).ToArray(); + string[] actual = [.. _reflectHelper.GetTestCategories(_method.Object, typeof(ReflectHelperTests))]; Verify(expected.SequenceEqual(actual)); } @@ -110,7 +110,7 @@ public void GetTestCategoryAttributeShouldIncludeMultipleTestCategoriesAtClassLe _attributeMockingHelper.SetCustomAttribute(typeof(TestCategoryBaseAttribute), [new TestCategoryAttribute("ClassLevel"), new TestCategoryAttribute("ClassLevel1")], MemberTypes.TypeInfo); string[] expected = ["ClassLevel", "ClassLevel1"]; - string[] actual = _reflectHelper.GetTestCategories(_method.Object, typeof(ReflectHelperTests)).ToArray(); + string[] actual = [.. _reflectHelper.GetTestCategories(_method.Object, typeof(ReflectHelperTests))]; Verify(expected.SequenceEqual(actual)); } @@ -123,7 +123,7 @@ public void GetTestCategoryAttributeShouldIncludeMultipleTestCategoriesAtAssembl _attributeMockingHelper.SetCustomAttribute(typeof(TestCategoryBaseAttribute), [new TestCategoryAttribute("AsmLevel"), new TestCategoryAttribute("AsmLevel1")], MemberTypes.All); string[] expected = ["AsmLevel", "AsmLevel1"]; - string[] actual = _reflectHelper.GetTestCategories(_method.Object, typeof(ReflectHelperTests)).ToArray(); + string[] actual = [.. _reflectHelper.GetTestCategories(_method.Object, typeof(ReflectHelperTests))]; Verify(expected.SequenceEqual(actual)); } @@ -135,7 +135,7 @@ public void GetTestCategoryAttributeShouldIncludeTestCategoriesAtMethodLevel() _attributeMockingHelper.SetCustomAttribute(typeof(TestCategoryBaseAttribute), [new TestCategoryAttribute("MethodLevel")], MemberTypes.Method); string[] expected = ["MethodLevel"]; - string[] actual = _reflectHelper.GetTestCategories(_method.Object, typeof(ReflectHelperTests)).ToArray(); + string[] actual = [.. _reflectHelper.GetTestCategories(_method.Object, typeof(ReflectHelperTests))]; Verify(expected.SequenceEqual(actual)); } @@ -150,7 +150,7 @@ public void IsAttributeDefinedShouldReturnTrueIfSpecifiedAttributeIsDefinedOnAMe Setup(ro => ro.GetCustomAttributes(mockMemberInfo.Object, true)). Returns(attributes); - Verify(rh.IsNonDerivedAttributeDefined(mockMemberInfo.Object, true)); + Verify(rh.IsAttributeDefined(mockMemberInfo.Object, true)); } public void IsAttributeDefinedShouldReturnFalseIfSpecifiedAttributeIsNotDefinedOnAMember() @@ -163,7 +163,7 @@ public void IsAttributeDefinedShouldReturnFalseIfSpecifiedAttributeIsNotDefinedO Setup(ro => ro.GetCustomAttributes(mockMemberInfo.Object, true)). Returns(attributes); - Verify(!rh.IsNonDerivedAttributeDefined(mockMemberInfo.Object, true)); + Verify(!rh.IsAttributeDefined(mockMemberInfo.Object, true)); } public void IsAttributeDefinedShouldReturnFromCache() @@ -180,10 +180,10 @@ public void IsAttributeDefinedShouldReturnFromCache() Setup(ro => ro.GetCustomAttributes(memberInfo, true)). Returns(attributes); - Verify(rh.IsNonDerivedAttributeDefined(memberInfo, true)); + Verify(rh.IsAttributeDefined(memberInfo, true)); // Validate that reflection APIs are not called again. - Verify(rh.IsNonDerivedAttributeDefined(memberInfo, true)); + Verify(rh.IsAttributeDefined(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. @@ -200,7 +200,7 @@ public void HasAttributeDerivedFromShouldReturnTrueIfSpecifiedAttributeIsDefined Setup(ro => ro.GetCustomAttributes(mockMemberInfo.Object, true)). Returns(attributes); - Verify(rh.IsDerivedAttributeDefined(mockMemberInfo.Object, true)); + Verify(rh.IsAttributeDefined(mockMemberInfo.Object, true)); } public void HasAttributeDerivedFromShouldReturnFalseIfSpecifiedAttributeIsNotDefinedOnAMember() @@ -213,7 +213,7 @@ public void HasAttributeDerivedFromShouldReturnFalseIfSpecifiedAttributeIsNotDef Setup(ro => ro.GetCustomAttributes(mockMemberInfo.Object, true)). Returns(attributes); - Verify(!rh.IsNonDerivedAttributeDefined(mockMemberInfo.Object, true)); + Verify(!rh.IsAttributeDefined(mockMemberInfo.Object, true)); } public void HasAttributeDerivedFromShouldReturnFromCache() @@ -230,10 +230,10 @@ public void HasAttributeDerivedFromShouldReturnFromCache() Setup(ro => ro.GetCustomAttributes(memberInfo, true)). Returns(attributes); - Verify(rh.IsDerivedAttributeDefined(memberInfo, true)); + Verify(rh.IsAttributeDefined(memberInfo, true)); // Validate that reflection APIs are not called again. - Verify(rh.IsDerivedAttributeDefined(memberInfo, true)); + Verify(rh.IsAttributeDefined(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. @@ -254,7 +254,7 @@ public void HasAttributeDerivedFromShouldReturnFalseQueryingProvidedAttributesEx Setup(ro => ro.GetCustomAttributes(mockMemberInfo.Object, typeof(TestMethodAttribute), true)). Returns(attributes); - Verify(!rh.IsNonDerivedAttributeDefined(mockMemberInfo.Object, true)); + Verify(!rh.IsAttributeDefined(mockMemberInfo.Object, true)); } public void GettingAttributesShouldNotReturnInheritedAttributesWhenAskingForNonInheritedAttributes() @@ -274,8 +274,8 @@ public void GettingAttributesShouldNotReturnInheritedAttributesWhenAskingForNonI Setup(ro => ro.GetCustomAttributes(It.IsAny(), /* inherit */ false)). Returns([new TestClassAttribute()]); - TestClassAttribute[] inheritedAttributes = rh.GetDerivedAttributes(typeof(object), inherit: true).ToArray(); - TestClassAttribute[] nonInheritedAttributes = rh.GetDerivedAttributes(typeof(object), inherit: false).ToArray(); + TestClassAttribute[] inheritedAttributes = [.. rh.GetAttributes(typeof(object), inherit: true)]; + TestClassAttribute[] nonInheritedAttributes = [.. rh.GetAttributes(typeof(object), inherit: false)]; Verify(inheritedAttributes.Length == 2); Verify(nonInheritedAttributes.Length == 1); @@ -291,7 +291,7 @@ internal class AttributeMockingHelper /// MemberTypes.TypeInfo for class level /// MemberTypes.Method for method level. /// - private readonly List<(Type Type, Attribute Attribute, MemberTypes MemberType)> _data = new(); + private readonly List<(Type Type, Attribute Attribute, MemberTypes MemberType)> _data = []; private readonly Mock _mockReflectionOperations; public void SetCustomAttribute(Type type, Attribute[] values, MemberTypes memberTypes) diff --git a/test/UnitTests/MSTestAdapter.UnitTests/Helpers/RunSettingsUtilitiesTests.cs b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Helpers/RunSettingsUtilitiesTests.cs similarity index 100% rename from test/UnitTests/MSTestAdapter.UnitTests/Helpers/RunSettingsUtilitiesTests.cs rename to test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Helpers/RunSettingsUtilitiesTests.cs diff --git a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Helpers/TestDataSourceHelpersTests.cs b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Helpers/TestDataSourceHelpersTests.cs new file mode 100644 index 0000000000..c470744a70 --- /dev/null +++ b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Helpers/TestDataSourceHelpersTests.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 Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Helpers; + +using TestFramework.ForTestingMSTest; + +namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.UnitTests.Helpers; + +public class TestDataSourceHelpersTests : TestContainer +{ + public void TryHandleITestDataRow_WithTestDataRow_ShouldExtractTestCategories() + { + // Arrange + var testData = new TestDataRow("test_value") + { + TestCategories = ["Category1", "Category2"], + IgnoreMessage = "ignore_message", + DisplayName = "display_name", + }; + object?[] dataArray = [testData]; + ParameterInfo[] parameters = []; // No method parameters for this test + + // Act + bool result = TestDataSourceHelpers.TryHandleITestDataRow( + dataArray, + parameters, + out object?[] extractedData, + out string? ignoreMessage, + out string? displayName, + out IList? testCategories); + + // Assert + Verify(result); + Verify(extractedData != null); + Verify(extractedData.Length == 1); + Verify((string?)extractedData[0] == "test_value"); + Verify(ignoreMessage == "ignore_message"); + Verify(displayName == "display_name"); + Verify(testCategories != null); + Verify(testCategories.Count == 2); + Verify(testCategories.Contains("Category1")); + Verify(testCategories.Contains("Category2")); + } + + public void TryHandleITestDataRow_WithTestDataRowNullCategories_ShouldReturnNullCategories() + { + // Arrange + var testData = new TestDataRow("test_value"); + object?[] dataArray = [testData]; + ParameterInfo[] parameters = []; + + // Act + bool result = TestDataSourceHelpers.TryHandleITestDataRow( + dataArray, + parameters, + out _, + out _, + out _, + out IList? testCategories); + + // Assert + Verify(result); + Verify(testCategories == null); + } + + public void TryHandleITestDataRow_WithNonTestDataRow_ShouldReturnFalseAndNullCategories() + { + // Arrange + object?[] dataArray = ["regular_string"]; + ParameterInfo[] parameters = []; + + // Act + bool result = TestDataSourceHelpers.TryHandleITestDataRow( + dataArray, + parameters, + out object?[] extractedData, + out string? ignoreMessage, + out string? displayName, + out IList? testCategories); + + // Assert + Verify(!result); + Verify(extractedData == dataArray); + Verify(ignoreMessage == null); + Verify(displayName == null); + Verify(testCategories == null); + } + + public void TryHandleITestDataRow_BackwardCompatibilityOverload_ShouldWork() + { + // Arrange + var testData = new TestDataRow("test_value") + { + TestCategories = ["Category1", "Category2"], + IgnoreMessage = "ignore_message", + DisplayName = "display_name", + }; + object?[] dataArray = [testData]; + ParameterInfo[] parameters = []; + + // Act + bool result = TestDataSourceHelpers.TryHandleITestDataRow( + dataArray, + parameters, + out object?[] extractedData, + out string? ignoreMessage, + out string? displayName); + + // Assert - should work without TestCategories parameter + Verify(result); + Verify(extractedData != null); + Verify(extractedData.Length == 1); + Verify((string?)extractedData[0] == "test_value"); + Verify(ignoreMessage == "ignore_message"); + Verify(displayName == "display_name"); + } +} diff --git a/test/UnitTests/MSTestAdapter.UnitTests/Helpers/UnitTestOutcomeHelperTests.cs b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Helpers/UnitTestOutcomeHelperTests.cs similarity index 100% rename from test/UnitTests/MSTestAdapter.UnitTests/Helpers/UnitTestOutcomeHelperTests.cs rename to test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Helpers/UnitTestOutcomeHelperTests.cs diff --git a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/MSTestAdapter.PlatformServices.UnitTests.csproj b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/MSTestAdapter.PlatformServices.UnitTests.csproj index 46cd16bf11..4065f07f1f 100644 --- a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/MSTestAdapter.PlatformServices.UnitTests.csproj +++ b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/MSTestAdapter.PlatformServices.UnitTests.csproj @@ -42,8 +42,6 @@ - - diff --git a/test/UnitTests/MSTestAdapter.UnitTests/MSTestSettingsTests.cs b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/MSTestSettingsTests.cs similarity index 99% rename from test/UnitTests/MSTestAdapter.UnitTests/MSTestSettingsTests.cs rename to test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/MSTestSettingsTests.cs index 688b87b1bb..8ddac24611 100644 --- a/test/UnitTests/MSTestAdapter.UnitTests/MSTestSettingsTests.cs +++ b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/MSTestSettingsTests.cs @@ -4,6 +4,7 @@ using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter; using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel; using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface; +using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Resources; using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.UnitTests.TestableImplementations; using Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter; using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging; @@ -11,6 +12,8 @@ using Moq; using TestFramework.ForTestingMSTest; + +using ExecutionScope = Microsoft.VisualStudio.TestTools.UnitTesting.ExecutionScope; #pragma warning disable CS0618 // Type or member is obsolete namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.UnitTests; @@ -563,7 +566,7 @@ public void GetSettingsShouldThrowWhenParallelizeHasInvalidElements() """; AdapterSettingsException exception = VerifyThrows(() => MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsNameAlias, _mockMessageLogger.Object)); - Verify(exception.Message.Contains("Invalid settings 'Parallelize'. Unexpected XmlElement: 'Hola'.")); + Verify(exception.Message.Contains("MSTestAdapter encountered an unexpected element 'Hola' in its settings 'Parallelize'. Remove this element and try again.")); } public void GetSettingsShouldBeAbleToReadAfterParallelizationSettings() diff --git a/test/UnitTests/MSTestAdapter.UnitTests/ObjectModel/UnitTestElementTests.cs b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/ObjectModel/UnitTestElementTests.cs similarity index 81% rename from test/UnitTests/MSTestAdapter.UnitTests/ObjectModel/UnitTestElementTests.cs rename to test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/ObjectModel/UnitTestElementTests.cs index 2985183fb9..27dc52885b 100644 --- a/test/UnitTests/MSTestAdapter.UnitTests/ObjectModel/UnitTestElementTests.cs +++ b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/ObjectModel/UnitTestElementTests.cs @@ -2,14 +2,13 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel; +using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices; using Microsoft.VisualStudio.TestPlatform.ObjectModel; using Polyfills; using TestFramework.ForTestingMSTest; -using Constants = Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Constants; - namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.UnitTests.ObjectModel; public class UnitTestElementTests : TestContainer @@ -43,7 +42,7 @@ public void ToTestCaseShouldSetExecutorUri() { var testCase = _unitTestElement.ToTestCase(); - Verify(testCase.ExecutorUri == Constants.ExecutorUri); + Verify(testCase.ExecutorUri == EngineConstants.ExecutorUri); } public void ToTestCaseShouldSetAssemblyName() @@ -72,7 +71,7 @@ public void ToTestCaseShouldSetTestClassNameProperty() { var testCase = _unitTestElement.ToTestCase(); - Verify((testCase.GetPropertyValue(Constants.TestClassNameProperty) as string) == "C"); + Verify((testCase.GetPropertyValue(EngineConstants.TestClassNameProperty) as string) == "C"); } public void ToTestCaseShouldSetDeclaringClassNameIfPresent() @@ -80,12 +79,12 @@ public void ToTestCaseShouldSetDeclaringClassNameIfPresent() _testMethod.DeclaringClassFullName = null; var testCase = _unitTestElement.ToTestCase(); - Verify(testCase.GetPropertyValue(Constants.DeclaringClassNameProperty) is null); + Verify(testCase.GetPropertyValue(EngineConstants.DeclaringClassNameProperty) is null); _testMethod.DeclaringClassFullName = "DC"; testCase = _unitTestElement.ToTestCase(); - Verify((testCase.GetPropertyValue(Constants.DeclaringClassNameProperty) as string) == "DC"); + Verify((testCase.GetPropertyValue(EngineConstants.DeclaringClassNameProperty) as string) == "DC"); } public void ToTestCaseShouldSetTestCategoryIfPresent() @@ -93,17 +92,17 @@ public void ToTestCaseShouldSetTestCategoryIfPresent() _unitTestElement.TestCategory = null; var testCase = _unitTestElement.ToTestCase(); - Verify(testCase.GetPropertyValue(Constants.TestCategoryProperty) is null); + Verify(testCase.GetPropertyValue(EngineConstants.TestCategoryProperty) is null); _unitTestElement.TestCategory = []; testCase = _unitTestElement.ToTestCase(); - Verify(testCase.GetPropertyValue(Constants.TestCategoryProperty) is null); + Verify(testCase.GetPropertyValue(EngineConstants.TestCategoryProperty) is null); _unitTestElement.TestCategory = ["TC"]; testCase = _unitTestElement.ToTestCase(); - Verify(new string[] { "TC" }.SequenceEqual((string[])testCase.GetPropertyValue(Constants.TestCategoryProperty)!)); + Verify(new string[] { "TC" }.SequenceEqual((string[])testCase.GetPropertyValue(EngineConstants.TestCategoryProperty)!)); } public void ToTestCaseShouldSetPriorityIfPresent() @@ -111,12 +110,12 @@ public void ToTestCaseShouldSetPriorityIfPresent() _unitTestElement.Priority = null; var testCase = _unitTestElement.ToTestCase(); - Verify((int)testCase.GetPropertyValue(Constants.PriorityProperty)! == 0); + Verify((int)testCase.GetPropertyValue(EngineConstants.PriorityProperty)! == 0); _unitTestElement.Priority = 1; testCase = _unitTestElement.ToTestCase(); - Verify((int)testCase.GetPropertyValue(Constants.PriorityProperty)! == 1); + Verify((int)testCase.GetPropertyValue(EngineConstants.PriorityProperty)! == 1); } public void ToTestCaseShouldSetTraitsIfPresent() @@ -141,15 +140,13 @@ public void ToTestCaseShouldSetPropertiesIfPresent() { _unitTestElement.CssIteration = "12"; _unitTestElement.CssProjectStructure = "ProjectStructure"; - _unitTestElement.Description = "I am a dummy test"; _unitTestElement.WorkItemIds = ["2312", "22332"]; var testCase = _unitTestElement.ToTestCase(); - Verify((testCase.GetPropertyValue(Constants.CssIterationProperty) as string) == "12"); - Verify((testCase.GetPropertyValue(Constants.CssProjectStructureProperty) as string) == "ProjectStructure"); - Verify((testCase.GetPropertyValue(Constants.DescriptionProperty) as string) == "I am a dummy test"); - Verify(new string[] { "2312", "22332" }.SequenceEqual((string[])testCase.GetPropertyValue(Constants.WorkItemIdsProperty)!)); + Verify((testCase.GetPropertyValue(EngineConstants.CssIterationProperty) as string) == "12"); + Verify((testCase.GetPropertyValue(EngineConstants.CssProjectStructureProperty) as string) == "ProjectStructure"); + Verify(new string[] { "2312", "22332" }.SequenceEqual((string[])testCase.GetPropertyValue(EngineConstants.WorkItemIdsProperty)!)); } public void ToTestCaseShouldSetDeploymentItemPropertyIfPresent() @@ -157,29 +154,29 @@ public void ToTestCaseShouldSetDeploymentItemPropertyIfPresent() _unitTestElement.DeploymentItems = null; var testCase = _unitTestElement.ToTestCase(); - Verify(testCase.GetPropertyValue(Constants.DeploymentItemsProperty) is null); + Verify(testCase.GetPropertyValue(EngineConstants.DeploymentItemsProperty) is null); _unitTestElement.DeploymentItems = []; testCase = _unitTestElement.ToTestCase(); - Verify(testCase.GetPropertyValue(Constants.DeploymentItemsProperty) is null); + Verify(testCase.GetPropertyValue(EngineConstants.DeploymentItemsProperty) is null); _unitTestElement.DeploymentItems = [new("s", "d")]; testCase = _unitTestElement.ToTestCase(); - Verify(_unitTestElement.DeploymentItems.SequenceEqual(testCase.GetPropertyValue(Constants.DeploymentItemsProperty) as KeyValuePair[])); + Verify(_unitTestElement.DeploymentItems.SequenceEqual(testCase.GetPropertyValue(EngineConstants.DeploymentItemsProperty) as KeyValuePair[])); } [Obsolete("Remove test case when enum entry is removed")] public void ToTestCase_WhenStrategyIsLegacy_UsesDefaultTestCaseId() { #pragma warning disable CA2263 // Prefer generic overload when type is known - foreach (DynamicDataType dataType in EnumPolyfill.GetValues()) + foreach (DynamicDataType dataType in Enum.GetValues()) { var testCase = new UnitTestElement(new("MyMethod", "MyProduct.MyNamespace.MyClass", "MyAssembly", null, TestIdGenerationStrategy.Legacy) { DataType = dataType }).ToTestCase(); var expectedTestCase = new TestCase(testCase.FullyQualifiedName, testCase.ExecutorUri, testCase.Source); Verify(expectedTestCase.Id == testCase.Id); - Verify(testCase.GetPropertyValue(Constants.TestIdGenerationStrategyProperty)!.Equals((int)TestIdGenerationStrategy.Legacy)); + Verify(testCase.GetPropertyValue(EngineConstants.TestIdGenerationStrategyProperty)!.Equals((int)TestIdGenerationStrategy.Legacy)); } #pragma warning restore CA2263 // Prefer generic overload when type is known } @@ -188,7 +185,7 @@ public void ToTestCase_WhenStrategyIsLegacy_UsesDefaultTestCaseId() public void ToTestCase_WhenStrategyIsDisplayName_DoesNotUseDefaultTestCaseId() { #pragma warning disable CA2263 // Prefer generic overload when type is known - foreach (DynamicDataType dataType in EnumPolyfill.GetValues()) + foreach (DynamicDataType dataType in Enum.GetValues()) { var testCase = new UnitTestElement(new("MyMethod", "MyProduct.MyNamespace.MyClass", "MyAssembly", null, TestIdGenerationStrategy.DisplayName) { DataType = dataType }).ToTestCase(); var expectedTestCase = new TestCase(testCase.FullyQualifiedName, testCase.ExecutorUri, testCase.Source); @@ -201,7 +198,7 @@ public void ToTestCase_WhenStrategyIsDisplayName_DoesNotUseDefaultTestCaseId() Verify(expectedTestCase.Id != testCase.Id); } - Verify(testCase.GetPropertyValue(Constants.TestIdGenerationStrategyProperty)!.Equals((int)TestIdGenerationStrategy.DisplayName)); + Verify(testCase.GetPropertyValue(EngineConstants.TestIdGenerationStrategyProperty)!.Equals((int)TestIdGenerationStrategy.DisplayName)); } #pragma warning restore CA2263 // Prefer generic overload when type is known } @@ -209,12 +206,12 @@ public void ToTestCase_WhenStrategyIsDisplayName_DoesNotUseDefaultTestCaseId() public void ToTestCase_WhenStrategyIsData_DoesNotUseDefaultTestCaseId() { #pragma warning disable CA2263 // Prefer generic overload when type is known - foreach (DynamicDataType dataType in EnumPolyfill.GetValues()) + foreach (DynamicDataType dataType in Enum.GetValues()) { var testCase = new UnitTestElement(new("MyMethod", "MyProduct.MyNamespace.MyClass", "MyAssembly", null, TestIdGenerationStrategy.FullyQualified) { DataType = dataType }).ToTestCase(); var expectedTestCase = new TestCase(testCase.FullyQualifiedName, testCase.ExecutorUri, testCase.Source); Verify(expectedTestCase.Id != testCase.Id); - Verify(testCase.GetPropertyValue(Constants.TestIdGenerationStrategyProperty)!.Equals((int)TestIdGenerationStrategy.FullyQualified)); + Verify(testCase.GetPropertyValue(EngineConstants.TestIdGenerationStrategyProperty)!.Equals((int)TestIdGenerationStrategy.FullyQualified)); } #pragma warning restore CA2263 // Prefer generic overload when type is known } @@ -240,7 +237,7 @@ public void ToTestCase_WhenStrategyIsDisplayName_ExamplesOfTestCaseIdUniqueness( new UnitTestElement( new("MyMethod", "MyProduct.MyNamespace.MyClass", "MyAssembly", null, testIdStrategy) { - DataType = DynamicDataType.DataSourceAttribute, + DataType = DynamicDataType.ITestDataSource, }) { DisplayName = "SomeDisplayName", @@ -269,20 +266,20 @@ public void ToTestCase_WhenStrategyIsDisplayName_ExamplesOfTestCaseIdCollision() new UnitTestElement( new("MyMethod", "MyProduct.MyNamespace.MyClass", "MyAssembly", null, testIdStrategy) { - DataType = DynamicDataType.DataSourceAttribute, + DataType = DynamicDataType.None, }) .ToTestCase(), new UnitTestElement( new("MyMethod", "MyProduct.MyNamespace.MyClass", "MyAssembly", null, testIdStrategy) { - DataType = DynamicDataType.DataSourceAttribute, + DataType = DynamicDataType.None, SerializedData = ["1"], }) .ToTestCase(), new UnitTestElement( new("MyMethod", "MyProduct.MyNamespace.MyClass", "MyAssembly", null, testIdStrategy) { - DataType = DynamicDataType.DataSourceAttribute, + DataType = DynamicDataType.None, SerializedData = ["2"], }) .ToTestCase(), @@ -302,7 +299,10 @@ public void ToTestCase_WhenStrategyIsDisplayName_ExamplesOfTestCaseIdCollision() .ToTestCase() ]; - Verify(testCases.Select(tc => tc.Id.ToString()).Distinct().Count() == 1); + // All the test cases with DynamicDataType.None will have the same Id (showing collisions). + // All the test cases with DynamicDataType.ITestDataSource will have the same Id, but different one (showing collisions). + // So for the 5 test cases, we have 2 distinct Ids. + Verify(testCases.Select(tc => tc.Id.ToString()).Distinct().Count() == 2); } public void ToTestCase_WhenStrategyIsFullyQualifiedTest_ExamplesOfTestCaseIdUniqueness() diff --git a/test/UnitTests/MSTestAdapter.UnitTests/ObjectModel/UnitTestResultTests.cs b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/ObjectModel/UnitTestResultTests.cs similarity index 100% rename from test/UnitTests/MSTestAdapter.UnitTests/ObjectModel/UnitTestResultTests.cs rename to test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/ObjectModel/UnitTestResultTests.cs diff --git a/test/UnitTests/MSTestAdapter.UnitTests/PlatformServiceProviderTests.cs b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/PlatformServiceProviderTests.cs similarity index 97% rename from test/UnitTests/MSTestAdapter.UnitTests/PlatformServiceProviderTests.cs rename to test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/PlatformServiceProviderTests.cs index 8d1f6f8884..2d4e5d21d4 100644 --- a/test/UnitTests/MSTestAdapter.UnitTests/PlatformServiceProviderTests.cs +++ b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/PlatformServiceProviderTests.cs @@ -58,13 +58,12 @@ public void GetTestContextShouldReturnAValidTestContext() { // Arrange. var testMethod = new Mock(); - var writer = new ThreadSafeStringWriter(null!, "test"); var properties = new Dictionary { { "prop", "value" } }; testMethod.Setup(tm => tm.FullClassName).Returns("A.C.M"); testMethod.Setup(tm => tm.Name).Returns("M"); // Act. - PlatformServices.Interface.ITestContext testContext = PlatformServiceProvider.Instance.GetTestContext(testMethod.Object, writer, properties, null!, default); + PlatformServices.Interface.ITestContext testContext = PlatformServiceProvider.Instance.GetTestContext(testMethod.Object, properties, null!, default); // Assert. Verify(testContext.Context.FullyQualifiedTestClassName == "A.C.M"); diff --git a/test/UnitTests/MSTestAdapter.UnitTests/RunConfigurationSettingsTests.cs b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/RunConfigurationSettingsTests.cs similarity index 100% rename from test/UnitTests/MSTestAdapter.UnitTests/RunConfigurationSettingsTests.cs rename to test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/RunConfigurationSettingsTests.cs diff --git a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Services/DesktopTestDeploymentTests.cs b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Services/DesktopTestDeploymentTests.cs index f7f7883ca2..d436619ee5 100644 --- a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Services/DesktopTestDeploymentTests.cs +++ b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Services/DesktopTestDeploymentTests.cs @@ -32,7 +32,7 @@ public DesktopTestDeploymentTests() { _mockReflectionUtility = new Mock(); _mockFileUtility = new Mock(); - _warnings = new List(); + _warnings = []; // Reset adapter settings. MSTestSettingsProvider.Reset(); diff --git a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Services/FileOperationsTests.cs b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Services/FileOperationsTests.cs index dfb8ee49d4..8d09fe5e9d 100644 --- a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Services/FileOperationsTests.cs +++ b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Services/FileOperationsTests.cs @@ -26,6 +26,20 @@ public void LoadAssemblyShouldThrowExceptionIfTheFileNameHasInvalidCharacters() #endif } + public void LoadAssemblyShouldNotThrowFileLoadExceptionIfTheFileNameHasValidFileCharacterButInvalidFullAssemblyNameCharacter() + { +#if NETCOREAPP + // = (for example) is a valid file name character, but not a valid character in an full assembly name. + // If we construct assembly name by calling new AssemblyName(filePath), it will throw FileLoadException for a correct file name. + // This test is checking that. It still fails with FileNotFoundException, because the file does not exist, but it should not throw FileLoadException. + // (The FileLoadException used for the unparseable name is weird choice, and confusing to me, but that is what the runtime decided to do. No dll is being loaded.) + string filePath = "temp=txt"; + void A() => _fileOperations.LoadAssembly(filePath, false); + + VerifyThrows(A); +#endif + } + public void LoadAssemblyShouldThrowExceptionIfFileIsNotFound() => VerifyThrows(() => _fileOperations.LoadAssembly("temptxt", false)); diff --git a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Services/MSTestSettingsProviderTests.cs b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Services/MSTestSettingsProviderTests.cs index be5a238fa1..874367ce73 100644 --- a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Services/MSTestSettingsProviderTests.cs +++ b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Services/MSTestSettingsProviderTests.cs @@ -2,6 +2,7 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices; +using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface; using Microsoft.VisualStudio.TestPlatform.ObjectModel.Utilities; using TestFramework.ForTestingMSTest; @@ -70,4 +71,32 @@ public void LoadShouldReadAndFillInSettings() _settingsProvider.Load(reader); Verify(!MSTestSettingsProvider.Settings.DeploymentEnabled); } + + public void LoadShouldReadAndFillInSettingsFromIConfiguration() + { + Verify(MSTestSettingsProvider.Settings.DeploymentEnabled); + + MSTestSettingsProvider.Load(new MockConfiguration( + new Dictionary() + { + ["mstest:deployment:enabled"] = "false", + }, null)); + + Verify(!MSTestSettingsProvider.Settings.DeploymentEnabled); + } + + private sealed class MockConfiguration : IConfiguration + { + private readonly Dictionary _values; + private readonly string? _defaultValue; + + public MockConfiguration(Dictionary values, string? defaultValue) + { + _values = values; + _defaultValue = defaultValue; + } + + public string? this[string key] + => _values.TryGetValue(key, out string? value) ? value : _defaultValue; + } } diff --git a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Services/ReflectionOperationsTests.cs b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Services/ReflectionOperationsTests.cs index dfd8626b64..97159226f6 100644 --- a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Services/ReflectionOperationsTests.cs +++ b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Services/ReflectionOperationsTests.cs @@ -67,9 +67,7 @@ public void GetCustomAttributesOnTypeShouldReturnAllAttributes() } private object[] GetMemberAttributes(Type type, bool inherit) - => _reflectionOperations.GetCustomAttributes(type, inherit) - .Where(x => x.GetType().FullName != "System.Runtime.CompilerServices.NullableContextAttribute") - .ToArray(); + => [.. _reflectionOperations.GetCustomAttributes(type, inherit).Where(x => x.GetType().FullName != "System.Runtime.CompilerServices.NullableContextAttribute")]; public void GetCustomAttributesOnTypeShouldReturnAllAttributesIgnoringBaseInheritance() { @@ -203,7 +201,7 @@ private static string[] GetAttributeValuePairs(object[] attributes) } } - return attribValuePairs.ToArray(); + return [.. attribValuePairs]; } [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class | AttributeTargets.Assembly, AllowMultiple = true)] diff --git a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Services/TestContextImplementationTests.cs b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Services/TestContextImplementationTests.cs index fdcfe307d2..5c457cf39e 100644 --- a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Services/TestContextImplementationTests.cs +++ b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Services/TestContextImplementationTests.cs @@ -7,6 +7,7 @@ #endif using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices; +using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Resources; using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging; using Moq; @@ -398,7 +399,7 @@ public void DisplayMessageShouldForwardToIMessageLogger() messageLoggerMock .Setup(l => l.SendMessage(It.IsAny(), It.IsAny())); - _testContextImplementation = new TestContextImplementation(_testMethod.Object, new ThreadSafeStringWriter(null!, "test"), _properties, messageLoggerMock.Object); + _testContextImplementation = new TestContextImplementation(_testMethod.Object, _properties, messageLoggerMock.Object, testRunCancellationToken: null); _testContextImplementation.DisplayMessage(MessageLevel.Informational, "InfoMessage"); _testContextImplementation.DisplayMessage(MessageLevel.Warning, "WarningMessage"); _testContextImplementation.DisplayMessage(MessageLevel.Error, "ErrorMessage"); @@ -407,4 +408,22 @@ public void DisplayMessageShouldForwardToIMessageLogger() messageLoggerMock.Verify(x => x.SendMessage(TestMessageLevel.Warning, "WarningMessage"), Times.Once); messageLoggerMock.Verify(x => x.SendMessage(TestMessageLevel.Error, "ErrorMessage"), Times.Once); } + + public void WritesFromBackgroundThreadShouldNotThrow() + { + var testContextImplementation = new TestContextImplementation(_testMethod.Object, _properties, new Mock().Object, testRunCancellationToken: null); + var t = new Thread(() => + { + for (int i = 0; i < 100; i++) + { + testContextImplementation.WriteConsoleOut(new string('a', 1000000)); + testContextImplementation.WriteConsoleErr(new string('b', 1000000)); + } + }); + + t.Start(); + _ = testContextImplementation.GetOut(); + _ = testContextImplementation.GetErr(); + t.Join(); + } } diff --git a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Services/TestDeploymentTests.cs b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Services/TestDeploymentTests.cs index 49b2bf0e66..37cf9f5605 100644 --- a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Services/TestDeploymentTests.cs +++ b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Services/TestDeploymentTests.cs @@ -33,7 +33,7 @@ public TestDeploymentTests() { _mockReflectionUtility = new Mock(); _mockFileUtility = new Mock(); - _warnings = new List(); + _warnings = []; // Reset adapter settings. MSTestSettingsProvider.Reset(); diff --git a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Services/ThreadSafeStringWriterTests.cs b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Services/ThreadSafeStringWriterTests.cs index a1c3c7f4ee..f58616a2cd 100644 --- a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Services/ThreadSafeStringWriterTests.cs +++ b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Services/ThreadSafeStringWriterTests.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. +#if false using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices; using TestFramework.ForTestingMSTest; @@ -127,3 +128,4 @@ public void ThreadSafeStringWriterWritesLinesIntoDifferentWritesSeparately() } } } +#endif diff --git a/test/UnitTests/MSTestAdapter.UnitTests/TestableImplementations/TestablePlatformServiceProvider.cs b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/TestableImplementations/TestablePlatformServiceProvider.cs similarity index 92% rename from test/UnitTests/MSTestAdapter.UnitTests/TestableImplementations/TestablePlatformServiceProvider.cs rename to test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/TestableImplementations/TestablePlatformServiceProvider.cs index 8cbc5c2de4..73acb57073 100644 --- a/test/UnitTests/MSTestAdapter.UnitTests/TestableImplementations/TestablePlatformServiceProvider.cs +++ b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/TestableImplementations/TestablePlatformServiceProvider.cs @@ -10,7 +10,6 @@ using ITestDataSource = Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface.ITestDataSource; using ITestMethod = Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface.ObjectModel.ITestMethod; -using UTF = Microsoft.VisualStudio.TestTools.UnitTesting; namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.UnitTests.TestableImplementations; @@ -70,9 +69,9 @@ public IReflectionOperations2 ReflectionOperations public bool IsGracefulStopRequested { get; set; } - public ITestContext GetTestContext(ITestMethod testMethod, StringWriter writer, IDictionary properties, IMessageLogger messageLogger, UTF.UnitTestOutcome outcome) + public ITestContext GetTestContext(ITestMethod testMethod, IDictionary properties, IMessageLogger messageLogger, UnitTestOutcome outcome) { - var testContextImpl = new TestContextImplementation(testMethod, writer, properties, messageLogger); + var testContextImpl = new TestContextImplementation(testMethod, properties, messageLogger, testRunCancellationToken: null); testContextImpl.SetOutcome(outcome); return testContextImpl; } diff --git a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Utilities/DeploymentUtilityTests.cs b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Utilities/DeploymentUtilityTests.cs index 673c1159d9..3ca5bd35f4 100644 --- a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Utilities/DeploymentUtilityTests.cs +++ b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Utilities/DeploymentUtilityTests.cs @@ -1,8 +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.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices; using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Deployment; +using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Resources; using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Utilities; using Microsoft.VisualStudio.TestPlatform.ObjectModel; using Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter; @@ -42,7 +42,7 @@ public DeploymentUtilityTests() _mockReflectionUtility = new Mock(); _mockFileUtility = new Mock(); _mockAssemblyUtility = new Mock(); - _warnings = new List(); + _warnings = []; _deploymentUtility = new DeploymentUtility( new DeploymentItemUtility(_mockReflectionUtility.Object), diff --git a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Utilities/DesktopReflectionUtilityTests.cs b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Utilities/DesktopReflectionUtilityTests.cs index 2e7348f8e9..80b9663729 100644 --- a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Utilities/DesktopReflectionUtilityTests.cs +++ b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Utilities/DesktopReflectionUtilityTests.cs @@ -40,7 +40,7 @@ internal static string[] GetAttributeValuePairs(IEnumerable attributes) } } - return attribValuePairs.ToArray(); + return [.. attribValuePairs]; } [DummyA("ba")] diff --git a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Utilities/VSInstallationUtilitiesTests.cs b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Utilities/VSInstallationUtilitiesTests.cs index 1f772ec276..5f836c7f96 100644 --- a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Utilities/VSInstallationUtilitiesTests.cs +++ b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Utilities/VSInstallationUtilitiesTests.cs @@ -14,7 +14,7 @@ public void CheckResolutionPathsDoNotContainPrivateAssembliesPathTest() { TestSourceHost isolatedHost = new(null!, null, null); List paths = isolatedHost.GetResolutionPaths(Assembly.GetExecutingAssembly().FullName, true); - Verify(!paths.Contains(Constants.PublicAssemblies) || paths.Contains(Constants.PrivateAssemblies)); + Verify(!paths.Contains(EngineConstants.PublicAssemblies) || paths.Contains(EngineConstants.PrivateAssemblies)); } } #endif diff --git a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Utilities/ns10FileUtilityTests.cs b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Utilities/ns10FileUtilityTests.cs index adea291520..5f83427888 100644 --- a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Utilities/ns10FileUtilityTests.cs +++ b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Utilities/ns10FileUtilityTests.cs @@ -105,10 +105,10 @@ public void AddFilesWithIgnoreDirectory() _fileUtility.Setup(fu => fu.GetDirectoriesInADirectory(It.IsAny())).Returns(directory => { IEnumerable directories = allFiles.Where(file => IsFileUnderDirectory(directory, file)).Select(file => Path.GetDirectoryName(file)!).Distinct(); - return directories.ToArray(); + return [.. directories]; }); - _fileUtility.Setup(fu => fu.GetFilesInADirectory(It.IsAny())).Returns(directory => allFiles.Where(file => Path.GetDirectoryName(file)!.Equals(directory, StringComparison.OrdinalIgnoreCase)).Distinct().ToArray()); + _fileUtility.Setup(fu => fu.GetFilesInADirectory(It.IsAny())).Returns(directory => [.. allFiles.Where(file => Path.GetDirectoryName(file)!.Equals(directory, StringComparison.OrdinalIgnoreCase)).Distinct()]); // Act List files = _fileUtility.Object.AddFilesFromDirectory("C:\\MainClock", directory => directory.Contains("Results"), false); @@ -145,10 +145,10 @@ public void AddFilesWithNoIgnoreDirectory() _fileUtility.Setup(fu => fu.GetDirectoriesInADirectory(It.IsAny())).Returns(directory => { IEnumerable directories = allFiles.Where(file => IsFileUnderDirectory(directory, file)).Select(file => Path.GetDirectoryName(file)!).Distinct(); - return directories.ToArray(); + return [.. directories]; }); - _fileUtility.Setup(fu => fu.GetFilesInADirectory(It.IsAny())).Returns(directory => allFiles.Where(file => Path.GetDirectoryName(file)!.Equals(directory, StringComparison.OrdinalIgnoreCase)).Distinct().ToArray()); + _fileUtility.Setup(fu => fu.GetFilesInADirectory(It.IsAny())).Returns(directory => [.. allFiles.Where(file => Path.GetDirectoryName(file)!.Equals(directory, StringComparison.OrdinalIgnoreCase)).Distinct()]); // Act List files = _fileUtility.Object.AddFilesFromDirectory("C:\\MainClock", false); @@ -171,9 +171,8 @@ private void SetupMockFileAPIs(string[] files) { _fileUtility.Setup(fu => fu.GetFilesInADirectory(It.IsAny())).Returns((string dp) => #pragma warning disable CA1865 // Use char overload - files.Where(f => f.Contains(dp) && f.LastIndexOf('\\') == (f.IndexOf(dp, StringComparison.Ordinal) + dp.Length) && !f.EndsWith("\\", StringComparison.Ordinal)) - .ToArray()); - _fileUtility.Setup(fu => fu.GetDirectoriesInADirectory(It.IsAny())).Returns((string dp) => files.Where(f => f.Contains(dp) && f.LastIndexOf('\\') > (f.IndexOf(dp, StringComparison.Ordinal) + dp.Length)) + [.. files.Where(f => f.Contains(dp) && f.LastIndexOf('\\') == (f.IndexOf(dp, StringComparison.Ordinal) + dp.Length) && !f.EndsWith("\\", StringComparison.Ordinal))]); + _fileUtility.Setup(fu => fu.GetDirectoriesInADirectory(It.IsAny())).Returns((string dp) => [.. files.Where(f => f.Contains(dp) && f.LastIndexOf('\\') > (f.IndexOf(dp, StringComparison.Ordinal) + dp.Length)) .Select(f => { #pragma warning disable IDE0057 // Use range operator @@ -183,8 +182,7 @@ private void SetupMockFileAPIs(string[] files) return f.Substring(0, dp.Length + 1 + val.IndexOf('\\')); #pragma warning restore IDE0057 // Use range operator }) - .Distinct() - .ToArray()); + .Distinct()]); } #endregion diff --git a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Utilities/ns13DeploymentItemUtilityTests.cs b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Utilities/ns13DeploymentItemUtilityTests.cs index 06ee4478ee..539c42f503 100644 --- a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Utilities/ns13DeploymentItemUtilityTests.cs +++ b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Utilities/ns13DeploymentItemUtilityTests.cs @@ -1,8 +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.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices; using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Deployment; +using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Resources; using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Utilities; using Microsoft.VisualStudio.TestPlatform.ObjectModel; @@ -34,7 +34,7 @@ public DeploymentItemUtilityTests() { _mockReflectionUtility = new Mock(); _deploymentItemUtility = new DeploymentItemUtility(_mockReflectionUtility.Object); - _warnings = new List(); + _warnings = []; } #region GetClassLevelDeploymentItems tests diff --git a/test/UnitTests/MSTestAdapter.UnitTests/DynamicDataAttributeTests.cs b/test/UnitTests/MSTestAdapter.UnitTests/DynamicDataAttributeTests.cs deleted file mode 100644 index dce014fc52..0000000000 --- a/test/UnitTests/MSTestAdapter.UnitTests/DynamicDataAttributeTests.cs +++ /dev/null @@ -1,531 +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.VisualStudio.TestPlatform.MSTest.TestAdapter; - -using TestFramework.ForTestingMSTest; - -namespace Microsoft.VisualStudio.TestPlatform.TestFramework.UnitTests.Attributes; - -public class DynamicDataAttributeTests : TestContainer -{ - private readonly DummyTestClass _dummyTestClass; - private readonly MethodInfo _testMethodInfo; - private DynamicDataAttribute _dynamicDataAttribute; - - public DynamicDataAttributeTests() - { - _dummyTestClass = new DummyTestClass(); - _testMethodInfo = _dummyTestClass.GetType().GetTypeInfo().GetDeclaredMethod("TestMethod1")!; - _dynamicDataAttribute = new DynamicDataAttribute("ReusableTestDataProperty"); - - // Initializes DynamicDataProvider. Normally this happens automatically but we are running outside of test adapter. - _ = PlatformServiceProvider.Instance; - DynamicDataAttribute.TestIdGenerationStrategy = TestIdGenerationStrategy.FullyQualified; - } - - public void GetDataShouldThrowExceptionIfInvalidPropertyNameIsSpecifiedOrPropertyDoesNotExist() - { - _dynamicDataAttribute = new DynamicDataAttribute("ABC"); - InvalidOperationException ex = VerifyThrows(() => _dynamicDataAttribute.GetData(_testMethodInfo)); - Verify(ex.Message == string.Format(CultureInfo.InvariantCulture, FrameworkMessages.DynamicDataSourceShouldExistAndBeValid, "ABC", _testMethodInfo.DeclaringType!.FullName)); - } - - public void GetDataShouldReadDataFromProperty() - { - MethodInfo methodInfo = _dummyTestClass.GetType().GetTypeInfo().GetDeclaredMethod("TestMethod1")!; - _dynamicDataAttribute = new DynamicDataAttribute("ReusableTestDataProperty"); - IEnumerable data = _dynamicDataAttribute.GetData(methodInfo); - Verify(data is not null); - Verify(data.ToList().Count == 2); - } - - public void GetDataShouldReadDataFromPropertyInDifferentClass() - { - MethodInfo methodInfo = _dummyTestClass.GetType().GetTypeInfo().GetDeclaredMethod("TestMethod1")!; - _dynamicDataAttribute = new DynamicDataAttribute("ReusableTestDataProperty2", typeof(DummyTestClass2)); - IEnumerable data = _dynamicDataAttribute.GetData(methodInfo); - Verify(data is not null); - Verify(data.ToList().Count == 2); - } - - public void GetDataShouldReadDataFromMethod() - { - MethodInfo methodInfo = _dummyTestClass.GetType().GetTypeInfo().GetDeclaredMethod("TestMethod2")!; - _dynamicDataAttribute = new DynamicDataAttribute("ReusableTestDataMethod", DynamicDataSourceType.Method); - IEnumerable data = _dynamicDataAttribute.GetData(methodInfo); - Verify(data is not null); - Verify(data.ToList().Count == 2); - } - - public void GetDataShouldReadDataFromMethodInDifferentClass() - { - MethodInfo methodInfo = _dummyTestClass.GetType().GetTypeInfo().GetDeclaredMethod("TestMethod2")!; - _dynamicDataAttribute = new DynamicDataAttribute("ReusableTestDataMethod2", typeof(DummyTestClass2), DynamicDataSourceType.Method); - IEnumerable data = _dynamicDataAttribute.GetData(methodInfo); - Verify(data is not null); - Verify(data.ToList().Count == 2); - } - - public void GetDataShouldThrowExceptionIfPropertyReturnsNull() => - VerifyThrows(() => - { - MethodInfo methodInfo = _dummyTestClass.GetType().GetTypeInfo().GetDeclaredMethod("TestMethod4")!; - _dynamicDataAttribute = new DynamicDataAttribute("NullProperty", typeof(DummyTestClass)); - _dynamicDataAttribute.GetData(methodInfo); - }); - - public void GetDataShouldNotThrowExceptionIfPropertyReturnsEmpty() - { - MethodInfo methodInfo = _dummyTestClass.GetType().GetTypeInfo().GetDeclaredMethod("TestMethod5")!; - _dynamicDataAttribute = new DynamicDataAttribute("EmptyProperty", typeof(DummyTestClass)); - IEnumerable data = _dynamicDataAttribute.GetData(methodInfo); - // The callers in AssemblyEnumerator and TestMethodRunner are responsible - // for throwing an exception if data is empty and ConsiderEmptyDataSourceAsInconclusive is false. - Verify(!data.Any()); - } - - public void GetDataShouldThrowExceptionIfPropertyDoesNotReturnCorrectType() => - VerifyThrows(() => - { - MethodInfo methodInfo = _dummyTestClass.GetType().GetTypeInfo().GetDeclaredMethod("TestMethod3")!; - _dynamicDataAttribute = new DynamicDataAttribute("WrongDataTypeProperty", typeof(DummyTestClass)); - _dynamicDataAttribute.GetData(methodInfo); - }); - - public void GetDisplayNameShouldReturnDisplayName() - { - object[] data = [1, 2, 3]; - - string? displayName = _dynamicDataAttribute.GetDisplayName(_testMethodInfo, data); - Verify("TestMethod1 (1,2,3)".SequenceEqual(displayName!)); - } - - public void GetDisplayNameShouldReturnDisplayNameWithDynamicDataDisplayName() - { - object[] data = [1, 2, 3]; - - _dynamicDataAttribute.DynamicDataDisplayName = "GetCustomDynamicDataDisplayName"; - string? displayName = _dynamicDataAttribute.GetDisplayName(_testMethodInfo, data); - Verify(displayName == "DynamicDataTestWithDisplayName TestMethod1 with 3 parameters"); - } - - public void GetDisplayNameShouldReturnDisplayNameWithDynamicDataDisplayNameInDifferentClass() - { - object[] data = [1, 2, 3]; - - _dynamicDataAttribute.DynamicDataDisplayName = "GetCustomDynamicDataDisplayName2"; - _dynamicDataAttribute.DynamicDataDisplayNameDeclaringType = typeof(DummyTestClass2); - string? displayName = _dynamicDataAttribute.GetDisplayName(_testMethodInfo, data); - Verify(displayName == "DynamicDataTestWithDisplayName TestMethod1 with 3 parameters"); - } - - public void GetDisplayNameShouldThrowExceptionWithDynamicDataDisplayNameMethodMissingParameters() => - VerifyThrows(() => - { - object[] data = [1, 2, 3]; - - _dynamicDataAttribute.DynamicDataDisplayName = "GetDynamicDataDisplayNameWithMissingParameters"; - _dynamicDataAttribute.GetDisplayName(_testMethodInfo, data); - }); - - public void GetDisplayNameShouldThrowExceptionWithDynamicDataDisplayNameMethodInvalidReturnType() => - VerifyThrows(() => - { - object[] data = [1, 2, 3]; - - _dynamicDataAttribute.DynamicDataDisplayName = "GetDynamicDataDisplayNameWithInvalidReturnType"; - _dynamicDataAttribute.GetDisplayName(_testMethodInfo, data); - }); - - public void GetDisplayNameShouldThrowExceptionWithDynamicDataDisplayNameMethodInvalidFirstParameterType() => - VerifyThrows(() => - { - object[] data = [1, 2, 3]; - - _dynamicDataAttribute.DynamicDataDisplayName = "GetDynamicDataDisplayNameWithInvalidFirstParameterType"; - _dynamicDataAttribute.GetDisplayName(_testMethodInfo, data); - }); - - public void GetDisplayNameShouldThrowExceptionWithDynamicDataDisplayNameMethodInvalidSecondParameterType() => - VerifyThrows(() => - { - object[] data = [1, 2, 3]; - - _dynamicDataAttribute.DynamicDataDisplayName = "GetDynamicDataDisplayNameWithInvalidSecondParameterType"; - _dynamicDataAttribute.GetDisplayName(_testMethodInfo, data); - }); - - public void GetDisplayNameShouldThrowExceptionWithDynamicDataDisplayNameMethodNonStatic() => - VerifyThrows(() => - { - object[] data = [1, 2, 3]; - - _dynamicDataAttribute.DynamicDataDisplayName = "GetDynamicDataDisplayNameNonStatic"; - _dynamicDataAttribute.GetDisplayName(_testMethodInfo, data); - }); - - public void GetDisplayNameShouldThrowExceptionWithDynamicDataDisplayNameMethodPrivate() => - VerifyThrows(() => - { - object[] data = [1, 2, 3]; - - _dynamicDataAttribute.DynamicDataDisplayName = "GetDynamicDataDisplayNamePrivate"; - _dynamicDataAttribute.GetDisplayName(_testMethodInfo, data); - }); - - public void GetDisplayNameShouldThrowExceptionWithMissingDynamicDataDisplayNameMethod() => - VerifyThrows(() => - { - object[] data = [1, 2, 3]; - - _dynamicDataAttribute.DynamicDataDisplayName = "MissingCustomDynamicDataDisplayName"; - _dynamicDataAttribute.GetDisplayName(_testMethodInfo, data); - }); - - public void GetDisplayNameShouldReturnEmptyStringIfDataIsNull() - { - string? displayName = _dynamicDataAttribute.GetDisplayName(_testMethodInfo, null); - Verify(displayName is null); - } - - public void GetDisplayNameHandlesNullValues() - { - 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\",null)"); - - displayName = _dynamicDataAttribute.GetDisplayName(_testMethodInfo, data1); - Verify(displayName == "TestMethod1 (null,\"value1\",\"value2\")"); - - displayName = _dynamicDataAttribute.GetDisplayName(_testMethodInfo, data2); - Verify(displayName == "TestMethod1 (\"value1\",null,\"value2\")"); - } - - public void GetDisplayNameForArrayOfMultipleItems() - { - string? displayName = _dynamicDataAttribute.GetDisplayName(_testMethodInfo, [new[] { "a", "b", "c" }]); - Verify(displayName == "TestMethod1 ([\"a\",\"b\",\"c\"])"); - } - - public void GetDisplayNameForMultipleArraysOfOneItem() - { - string? displayName = _dynamicDataAttribute.GetDisplayName(_testMethodInfo, [new[] { "a" }, new[] { "1" }]); - Verify(displayName == "TestMethod1 ([\"a\"],[\"1\"])"); - } - - public void GetDisplayNameForMultipleArraysOfMultipleItems() - { - string? displayName = _dynamicDataAttribute.GetDisplayName(_testMethodInfo, [new[] { "a", "b", "c" }, new[] { "1", "2", "3" }]); - Verify(displayName == "TestMethod1 ([\"a\",\"b\",\"c\"],[\"1\",\"2\",\"3\"])"); - } - - public void GetDisplayNameForMultipleArraysOfMultipleItemsValueTypes() - { - string? displayName = _dynamicDataAttribute.GetDisplayName(_testMethodInfo, [new[] { 1, 2, 3 }, new[] { 4, 5, 6 }]); - Verify(displayName == "TestMethod1 ([1,2,3],[4,5,6])"); - } - - public void GetDisplayNameForMultipleArraysOfArraysOfMultipleItems() - { - string? displayName = _dynamicDataAttribute.GetDisplayName(_testMethodInfo, [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" } }]); - Verify(displayName == "TestMethod1 ([[\"a\",\"b\",\"c\"],[\"d\",\"e\",\"f\"],[\"gh\",\"ij\",\"kl\"]],['m','n','o'],[[\"1\",\"2\",\"3\"],[\"4\",\"5\",\"6\"],[\"7\",\"8\",\"9\"]])"); - } - - public void DynamicDataSource_WithTuple_Works() - { - MethodInfo testMethodInfo = new TestClassTupleData().GetType().GetTypeInfo().GetDeclaredMethod(nameof(TestClassTupleData.DynamicDataTestWithTuple))!; - var dynamicDataAttribute = new DynamicDataAttribute(nameof(TestClassTupleData.DataWithTuple), typeof(TestClassTupleData)); - dynamicDataAttribute.GetData(testMethodInfo); - - dynamicDataAttribute = new DynamicDataAttribute(nameof(TestClassTupleData.GetDataWithTuple), typeof(TestClassTupleData), DynamicDataSourceType.Method); - 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)); - dynamicDataAttribute.GetData(testMethodInfo); - - dynamicDataAttribute = new DynamicDataAttribute(nameof(TestClassTupleData.GetDataWithValueTuple), typeof(TestClassTupleData), DynamicDataSourceType.Method); - 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)); - dynamicDataAttribute.GetData(testMethodInfo); - - dynamicDataAttribute = new DynamicDataAttribute(nameof(TestClassTupleData.GetDataWithValueTupleWithTupleSyntax), typeof(TestClassTupleData), DynamicDataSourceType.Method); - dynamicDataAttribute.GetData(testMethodInfo); - } -} - -/// -/// The dummy test class. -/// -[TestClass] -internal class DummyTestClass -{ - /// - /// Gets the reusable test data property. - /// - public static IEnumerable ReusableTestDataProperty => [[1, 2, 3], [4, 5, 6]]; - - /// - /// Gets the null test data property. - /// - public static IEnumerable NullProperty => null!; - - /// - /// Gets the empty test data property. - /// - public static IEnumerable EmptyProperty => Array.Empty(); - - /// - /// Gets the wrong test data property i.e. Property returning something other than - /// expected data type of . - /// - public static string WrongDataTypeProperty => "Dummy"; - - /// - /// The reusable test data method. - /// - /// - /// The . - /// - public static IEnumerable ReusableTestDataMethod() => [[1, 2, 3], [4, 5, 6]]; - - /// - /// The custom display name method. - /// - /// - /// The method info of test method. - /// - /// - /// The test data which is passed to test method. - /// - /// - /// The . - /// - public static string GetCustomDynamicDataDisplayName(MethodInfo methodInfo, object[] data) - => $"DynamicDataTestWithDisplayName {methodInfo.Name} with {data.Length} parameters"; - - /// - /// Custom display name method with missing parameters. - /// - /// - /// The . - /// - public static string GetDynamicDataDisplayNameWithMissingParameters() => throw new InvalidOperationException(); - - /// - /// Custom display name method with invalid return type. - /// - public static void GetDynamicDataDisplayNameWithInvalidReturnType() => throw new InvalidOperationException(); - - /// - /// Custom display name method with invalid first parameter type. - /// - /// - /// The method info of test method. - /// - /// - /// The test data which is passed to test method. - /// - /// - /// The . - /// - public static string GetDynamicDataDisplayNameWithInvalidFirstParameterType(string methodInfo, object[] data) => throw new InvalidOperationException(); - - /// - /// Custom display name method with invalid second parameter. - /// - /// - /// The method info of test method. - /// - /// - /// The test data which is passed to test method. - /// - /// - /// The . - /// - public static string GetDynamicDataDisplayNameWithInvalidSecondParameterType(MethodInfo methodInfo, string data) => throw new InvalidOperationException(); - - /// - /// Custom display name method that is not static. - /// - /// - /// The method info of test method. - /// - /// - /// The test data which is passed to test method. - /// - /// - /// The . - /// - public string GetDynamicDataDisplayNameNonStatic(MethodInfo methodInfo, object[] data) => throw new InvalidOperationException(); - - /// - /// The test method 1. - /// - [TestMethod] - [DynamicData("ReusableTestDataProperty")] - public void TestMethod1() - { - } - - /// - /// The test method 2. - /// - [TestMethod] - [DynamicData("ReusableTestDataMethod")] - public void TestMethod2() - { - } - - /// - /// The test method 3. - /// - [TestMethod] - [DynamicData("WrongDataTypeProperty")] - public void TestMethod3() - { - } - - /// - /// The test method 4. - /// - [TestMethod] - [DynamicData("NullProperty")] - public void TestMethod4() - { - } - - /// - /// The test method 5. - /// - [TestMethod] - [DynamicData("EmptyProperty")] - public void TestMethod5() - { - } - - /// - /// DataRow test method 1. - /// - [DataRow("First", "Second", null)] - [DataRow(null, "First", "Second")] - [DataRow("First", null, "Second")] - [TestMethod] - public void DataRowTestMethod() - { - } - - /// - /// Custom display name method that is private. - /// - /// - /// The method info of test method. - /// - /// - /// The test data which is passed to test method. - /// - /// - /// The . - /// -#pragma warning disable IDE0051 // Remove unused private members - private static string GetDynamicDataDisplayNamePrivate(MethodInfo methodInfo, object[] data) => throw new InvalidOperationException(); -} - -public class DummyTestClass2 -{ - /// - /// Gets the reusable test data property. - /// - public static IEnumerable ReusableTestDataProperty2 => [[1, 2, 3], [4, 5, 6]]; - - /// - /// The reusable test data method. - /// - /// - /// The . - /// - public static IEnumerable ReusableTestDataMethod2() => [[1, 2, 3], [4, 5, 6]]; - - /// - /// The custom display name method. - /// - /// - /// The method info of test method. - /// - /// - /// The test data which is passed to test method. - /// - /// - /// The . - /// - public static string GetCustomDynamicDataDisplayName2(MethodInfo methodInfo, object[] data) - => $"DynamicDataTestWithDisplayName {methodInfo.Name} with {data.Length} parameters"; -} - -[TestClass] -internal class TestClassTupleData -{ - public static IEnumerable> GetDataWithTuple() - { - yield return new(0, "0"); - yield return new(1, "1"); - } - - public static IEnumerable> DataWithTuple - { - get - { - yield return new(0, "0"); - yield return new(1, "1"); - } - } - - [SuppressMessage("StyleCop.CSharp.ReadabilityRules", "SA1141:Use tuple syntax", Justification = "We want to explicitly test this syntax")] - public static IEnumerable> GetDataWithValueTuple() - { - yield return new(0, "0"); - yield return new(1, "1"); - } - - [SuppressMessage("StyleCop.CSharp.ReadabilityRules", "SA1141:Use tuple syntax", Justification = "We want to explicitly test this syntax")] - public static IEnumerable> DataWithValueTuple - { - get - { - yield return new(0, "0"); - yield return new(1, "1"); - } - } - - public static IEnumerable<(int Integer, string AsString)> GetDataWithValueTupleWithTupleSyntax() - { - yield return (0, "0"); - yield return (1, "1"); - } - - public static IEnumerable<(int Integer, string AsString)> DataWithValueTupleWithTupleSyntax - { - get - { - yield return (0, "0"); - yield return (1, "1"); - } - } - - [DataTestMethod] - public void DynamicDataTestWithTuple(int value, string integerAsString) - { - } -} diff --git a/test/UnitTests/MSTestAdapter.UnitTests/Extensions/ExceptionExtensionsTests.cs b/test/UnitTests/MSTestAdapter.UnitTests/Extensions/ExceptionExtensionsTests.cs deleted file mode 100644 index 4bea35ee12..0000000000 --- a/test/UnitTests/MSTestAdapter.UnitTests/Extensions/ExceptionExtensionsTests.cs +++ /dev/null @@ -1,209 +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.VisualStudio.TestPlatform.MSTest.TestAdapter; -using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Extensions; - -using TestFramework.ForTestingMSTest; - -using UTF = Microsoft.VisualStudio.TestTools.UnitTesting; - -namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.UnitTests.Extensions; - -/// -/// Tests for class. -/// -public class ExceptionExtensionsTests : TestContainer -{ - #region TryGetExceptionMessage scenarios - - public void ExceptionTryGetMessageGetsTheExceptionMessage() - { - var exception = new Exception("dummyMessage"); - - Verify(exception.TryGetMessage() == "dummyMessage"); - } - - public void ExceptionTryGetMessageReturnsEmptyStringIfExceptionMessageIsNull() - { - var exception = new DummyException(() => null!); - - Verify(exception.TryGetMessage() == string.Empty); - } - - public void ExceptionTryGetMessageReturnsErrorMessageIfExceptionIsNull() - { - string errorMessage = string.Format(CultureInfo.InvariantCulture, Resource.UTF_FailedToGetExceptionMessage, "null"); - - var exception = (Exception?)null; - - Verify(errorMessage == exception.TryGetMessage()); - } - - public void ExceptionTryGetMessageShouldThrowIfExceptionMessageThrows() - { - var exception = new DummyException(() => throw new NotImplementedException()); - - VerifyThrows(() => exception.TryGetMessage()); - } - - #endregion - - #region TryGetStackTraceInformation scenarios - - public void TryGetStackTraceInformationReturnsNullIfExceptionStackTraceIsNullOrEmpty() - { - var exception = new DummyExceptionForStackTrace(() => null!); - - Verify(exception.TryGetStackTraceInformation() is null); - } - - public void TryGetStackTraceInformationReturnsStackTraceForAnException() - { - var exception = new DummyExceptionForStackTrace(() => " at A()\r\n at B()"); - - MSTest.TestAdapter.ObjectModel.StackTraceInformation? stackTraceInformation = exception.TryGetStackTraceInformation(); - - Verify(stackTraceInformation!.ErrorStackTrace.StartsWith(" at A()", StringComparison.Ordinal)); - Verify(stackTraceInformation.ErrorFilePath is null); - Verify(stackTraceInformation.ErrorLineNumber == 0); - } - - public void TryGetStackTraceInformationShouldThrowIfStackTraceThrows() - { - var exception = new DummyExceptionForStackTrace(() => throw new NotImplementedException()); - - VerifyThrows(() => exception.TryGetStackTraceInformation()); - } - -#pragma warning disable CA1710 // Identifiers should have correct suffix - public class DummyExceptionForStackTrace : Exception -#pragma warning restore CA1710 // Identifiers should have correct suffix - { - private readonly Func _getStackTrace; - - public DummyExceptionForStackTrace(Func getStackTrace) => _getStackTrace = getStackTrace; - - public override string StackTrace => _getStackTrace(); - } - - internal class DummyException : Exception - { - private readonly Func _getMessage; - - public DummyException(Func message) => _getMessage = message; - - public override string Message => _getMessage(); - } - - #endregion - - #region IsUnitTestAssertException scenarios - - public void IsUnitTestAssertExceptionReturnsTrueIfExceptionIsAssertException() - { - var exception = new UTF.AssertInconclusiveException(); - Verify(exception.TryGetUnitTestAssertException(out _, out _, out _)); - } - - public void IsUnitTestAssertExceptionReturnsFalseIfExceptionIsNotAssertException() - { - var exception = new NotImplementedException(); - Verify(!exception.TryGetUnitTestAssertException(out _, out _, out _)); - } - - public void IsUnitTestAssertExceptionSetsOutcomeAsInconclusiveIfAssertInconclusiveException() - { - var exception = new UTF.AssertInconclusiveException("Dummy Message", new NotImplementedException("notImplementedException")); - exception.TryGetUnitTestAssertException(out UTF.UnitTestOutcome outcome, out string? exceptionMessage, out _); - - Verify(outcome == UTF.UnitTestOutcome.Inconclusive); - Verify(exceptionMessage == "Dummy Message"); - } - - public void IsUnitTestAssertExceptionSetsOutcomeAsFailedIfAssertFailedException() - { - var exception = new UTF.AssertFailedException("Dummy Message", new NotImplementedException("notImplementedException")); - exception.TryGetUnitTestAssertException(out UTF.UnitTestOutcome outcome, out string? exceptionMessage, out _); - - Verify(outcome == UTF.UnitTestOutcome.Failed); - Verify(exceptionMessage == "Dummy Message"); - } - #endregion - - #region GetRealException scenarios - public void GetRealExceptionGetsTheTopExceptionWhenThereIsJustOne() - { - var exception = new InvalidOperationException(); - Exception actual = exception.GetRealException(); - - Verify(actual is InvalidOperationException); - } - - public void GetRealExceptionGetsTheInnerExceptionWhenTheExceptionIsTargetInvocation() - { - var exception = new TargetInvocationException(new InvalidOperationException()); - Exception actual = exception.GetRealException(); - - Verify(actual is InvalidOperationException); - } - - public void GetRealExceptionGetsTheTargetInvocationExceptionWhenTargetInvocationIsProvidedWithNullInnerException() - { - var exception = new TargetInvocationException(null); - Exception actual = exception.GetRealException(); - - Verify(actual is TargetInvocationException); - } - - public void GetRealExceptionGetsTheInnerMostRealException() - { - var exception = new TargetInvocationException(new TargetInvocationException(new TargetInvocationException(new InvalidOperationException()))); - Exception actual = exception.GetRealException(); - - Verify(actual is InvalidOperationException); - } - - public void GetRealExceptionGetsTheInnerMostTargetInvocationException() - { - var exception = new TargetInvocationException(new TargetInvocationException(new TargetInvocationException("inner most", null))); - Exception actual = exception.GetRealException(); - - Verify(actual is TargetInvocationException); - Verify(actual.Message == "inner most"); - } - - public void GetRealExceptionGetsTheInnerExceptionWhenTheExceptionIsTypeInitialization() - { - var exception = new TypeInitializationException("some type", new InvalidOperationException()); - Exception actual = exception.GetRealException(); - - Verify(actual is InvalidOperationException); - } - - public void GetRealExceptionGetsTheTypeInitializationExceptionWhenTypeInitializationIsProvidedWithNullInnerException() - { - var exception = new TypeInitializationException("some type", null); - Exception actual = exception.GetRealException(); - - Verify(actual is TypeInitializationException); - } - - public void GetRealExceptionGetsTheInnerMostRealExceptionOfTypeInitialization() - { - var exception = new TypeInitializationException("some type", new TypeInitializationException("some type", new TypeInitializationException("some type", new InvalidOperationException()))); - Exception actual = exception.GetRealException(); - - Verify(actual is InvalidOperationException); - } - - public void GetRealExceptionGetsTheInnerMostTypeInitializationException() - { - var exception = new TypeInitializationException("some type", new TypeInitializationException("some type", new TypeInitializationException("inner most", null))); - Exception actual = exception.GetRealException(); - - Verify(actual is TypeInitializationException); - Verify(actual.Message == "The type initializer for 'inner most' threw an exception."); - } - #endregion -} diff --git a/test/UnitTests/MSTestAdapter.UnitTests/MSTestAdapter.UnitTests.csproj b/test/UnitTests/MSTestAdapter.UnitTests/MSTestAdapter.UnitTests.csproj index 2f3eddd243..cb3f94b7b0 100644 --- a/test/UnitTests/MSTestAdapter.UnitTests/MSTestAdapter.UnitTests.csproj +++ b/test/UnitTests/MSTestAdapter.UnitTests/MSTestAdapter.UnitTests.csproj @@ -2,7 +2,7 @@ net48 - net6.0;net462;$(NetStandardNetFrameworkHolder);netcoreapp3.1 + net6.0;net7.0;net462;$(NetStandardNetFrameworkHolder);netcoreapp3.1 $(TargetFrameworks);$(WinUiMinimum) true Microsoft.VisualStudio.TestPlatform.MSTestAdapter.UnitTests @@ -21,6 +21,10 @@ + + + + @@ -34,8 +38,6 @@ - - diff --git a/test/UnitTests/MSTestAdapter.UnitTests/MSTestDiscovererTests.cs b/test/UnitTests/MSTestAdapter.UnitTests/MSTestDiscovererTests.cs index c738849994..ea65306c21 100644 --- a/test/UnitTests/MSTestAdapter.UnitTests/MSTestDiscovererTests.cs +++ b/test/UnitTests/MSTestAdapter.UnitTests/MSTestDiscovererTests.cs @@ -226,4 +226,15 @@ public void AreValidSourcesShouldReturnFalseForInvalidSourceExtensions() _testablePlatformServiceProvider.MockTestSourceValidator.SetupGet(ts => ts.ValidSourceExtensions).Returns(new List { ".nte", ".tep" }); Verify(!MSTestDiscovererHelpers.AreValidSources(new List { "dummy.te" })); } + + [TestClass] + public class DummyClass + { + [TestMethod] + public void DummyTestMethod() + { + // This is a dummy test method to ensure that the MSTestDiscoverer can discover tests. + // It does not perform any assertions or operations. + } + } } diff --git a/test/UnitTests/MSTestAdapter.UnitTests/MSTestExecutorTests.cs b/test/UnitTests/MSTestAdapter.UnitTests/MSTestExecutorTests.cs index 65ac7fc0d9..ca7774fca5 100644 --- a/test/UnitTests/MSTestAdapter.UnitTests/MSTestExecutorTests.cs +++ b/test/UnitTests/MSTestAdapter.UnitTests/MSTestExecutorTests.cs @@ -2,6 +2,7 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter; +using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices; using Microsoft.VisualStudio.TestPlatform.ObjectModel; using Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter; @@ -32,7 +33,7 @@ public void MSTestExecutorShouldProvideTestExecutionUri() var extensionUriString = (ExtensionUriAttribute)testExecutor.GetType().GetCustomAttributes(typeof(ExtensionUriAttribute), false).Single(); - Verify(extensionUriString.ExtensionUri == MSTest.TestAdapter.Constants.ExecutorUriString); + Verify(extensionUriString.ExtensionUri == EngineConstants.ExecutorUriString); } public async Task RunTestsShouldNotExecuteTestsIfTestSettingsIsGiven() diff --git a/test/UnitTests/Microsoft.Testing.Extensions.UnitTests/AppInsightsProviderTests.cs b/test/UnitTests/Microsoft.Testing.Extensions.UnitTests/AppInsightsProviderTests.cs index 95068374c5..c281ef4a14 100644 --- a/test/UnitTests/Microsoft.Testing.Extensions.UnitTests/AppInsightsProviderTests.cs +++ b/test/UnitTests/Microsoft.Testing.Extensions.UnitTests/AppInsightsProviderTests.cs @@ -32,7 +32,7 @@ public void Platform_CancellationToken_Cancellation_Should_Exit_Gracefully() Mock testApplicationCancellationTokenSource = new(); testApplicationCancellationTokenSource.Setup(x => x.CancellationToken).Returns(cancellationTokenSource.Token); - List events = new(); + List events = []; Mock testTelemetryClient = new(); testTelemetryClient.Setup(x => x.TrackEvent(It.IsAny(), It.IsAny>(), It.IsAny>())) .Callback((string eventName, Dictionary properties, Dictionary metrics) => diff --git a/test/UnitTests/Microsoft.Testing.Extensions.UnitTests/Helpers/TestExtension.cs b/test/UnitTests/Microsoft.Testing.Extensions.UnitTests/Helpers/TestExtension.cs index 85bdd02fc3..5019e8d619 100644 --- a/test/UnitTests/Microsoft.Testing.Extensions.UnitTests/Helpers/TestExtension.cs +++ b/test/UnitTests/Microsoft.Testing.Extensions.UnitTests/Helpers/TestExtension.cs @@ -5,13 +5,13 @@ namespace Microsoft.Testing.Extensions.UnitTests.Helpers; internal sealed class TestExtension : IExtension { - public string Uid { get; } = "Uid"; + public string Uid => "Uid"; - public string Version { get; } = "Version"; + public string Version => "Version"; - public string DisplayName { get; } = "DisplayName"; + public string DisplayName => "DisplayName"; - public string Description { get; } = "Description"; + public string Description => "Description"; public Task IsEnabledAsync() => Task.FromResult(true); } diff --git a/test/UnitTests/Microsoft.Testing.Extensions.UnitTests/TrxTests.cs b/test/UnitTests/Microsoft.Testing.Extensions.UnitTests/TrxTests.cs index 0f1a96c59d..d168f4be72 100644 --- a/test/UnitTests/Microsoft.Testing.Extensions.UnitTests/TrxTests.cs +++ b/test/UnitTests/Microsoft.Testing.Extensions.UnitTests/TrxTests.cs @@ -27,7 +27,7 @@ public class TrxTests private readonly Mock _testFrameworkMock = new(); private readonly Mock _testApplicationModuleInfoMock = new(); private readonly Mock _fileSystem = new(); - private readonly Dictionary> _artifactsByExtension = new(); + private readonly Dictionary> _artifactsByExtension = []; [TestMethod] public async Task TrxReportEngine_GenerateReportAsyncWithNullAdapterSupportTrxCapability_TrxDoesNotContainClassName() @@ -47,7 +47,7 @@ public async Task TrxReportEngine_GenerateReportAsyncWithNullAdapterSupportTrxCa XDocument xml = memoryStream.TrxContent; AssertTrxOutcome(xml, "Completed"); string trxContent = xml.ToString(); - Assert.IsFalse(trxContent.Contains(@"className=")); + Assert.DoesNotContain(@"className=", trxContent); } [TestMethod] @@ -68,7 +68,7 @@ public async Task TrxReportEngine_GenerateReportAsyncWithNotExecutedTests_TrxExe XDocument xml = memoryStream.TrxContent; AssertTrxOutcome(xml, "Completed"); string trxContent = xml.ToString(); - Assert.IsTrue(trxContent.Contains(@"notExecuted=""1""")); + Assert.Contains(@"notExecuted=""1""", trxContent); } [TestMethod] @@ -89,7 +89,7 @@ public async Task TrxReportEngine_GenerateReportAsyncWithTimeoutTests_TrxTimeout XDocument xml = memoryStream.TrxContent; AssertTrxOutcome(xml, "Completed"); string trxContent = xml.ToString(); - Assert.IsTrue(trxContent.Contains(@"timeout=""1""")); + Assert.Contains(@"timeout=""1""", trxContent); } [TestMethod] @@ -122,7 +122,7 @@ public async Task TrxReportEngine_GenerateReportAsync_WithArgumentTrxReportFileN _ = _commandLineOptionsMock.Setup(_ => _.TryGetOptionArgumentList(TrxReportGeneratorCommandLine.TrxReportFileNameOptionName, out argumentTrxReportFileName)).Returns(true); PropertyBag propertyBag = new(new PassedTestNodeStateProperty()); TrxReportEngine trxReportEngine = GenerateTrxReportEngine(1, 0, propertyBag, memoryStream, isExplicitFileName: true); - _ = _fileSystem.Setup(x => x.Exists(It.IsAny())).Returns(true); + _ = _fileSystem.Setup(x => x.ExistFile(It.IsAny())).Returns(true); // Act (string fileName, string? warning) = await trxReportEngine.GenerateReportAsync(); @@ -405,7 +405,7 @@ public async Task TrxReportEngine_GenerateReportAsync_WithAdapterSupportTrxCapab XDocument xml = memoryStream.TrxContent; AssertTrxOutcome(xml, "Completed"); string trxContent = xml.ToString(); - Assert.IsTrue(trxContent.Contains(@"className=""TrxFullyQualifiedTypeName"), trxContent); + Assert.Contains(@"className=""TrxFullyQualifiedTypeName", trxContent, trxContent); } [TestMethod] @@ -503,6 +503,53 @@ public async Task TrxReportEngine_GenerateReportAsync_FileAlreadyExists_WillRetr Assert.AreEqual(4, retryCount); } + [TestMethod] + public async Task TrxReportEngine_GenerateReportAsync_WithMetadataProperties_TrxHandlesProperties() + { + // Arrange + using MemoryFileStream memoryStream = new(); + TrxReportEngine trxReportEngine = GenerateTrxReportEngine(1, 0, + new( + new PassedTestNodeStateProperty(), + new TestMetadataProperty("Owner", "ValueOfOwner"), + new TestMetadataProperty("Description", "Description of my test"), + new TestMetadataProperty("Priority", "5"), + new TestMetadataProperty("MyProperty1", "MyValue1"), + new TestMetadataProperty("MyProperty2", "MyValue2")), memoryStream); + + // Act + (string fileName, string? warning) = await trxReportEngine.GenerateReportAsync(); + + // Assert + Assert.IsNull(warning); + AssertExpectedTrxFileName(fileName); + Assert.IsNotNull(memoryStream.TrxContent); + XDocument xml = memoryStream.TrxContent; + AssertTrxOutcome(xml, "Completed"); + string trxContent = xml.ToString(); + string trxContentsPattern = @" + + + + + + Description of my test + + + MyProperty2 + MyValue2 + + + MyProperty1 + MyValue1 + + + + + "; + Assert.IsTrue(Regex.IsMatch(trxContent, trxContentsPattern)); + } + private static void AssertTrxOutcome(XDocument xml, string expectedOutcome) { Assert.IsNotNull(xml); @@ -516,7 +563,7 @@ private static void AssertTrxOutcome(XDocument xml, string expectedOutcome) } private static void AssertExpectedTrxFileName(string fileName) - => Assert.IsTrue(fileName.Equals("_MachineName_0001-01-01_00_00_00.000.trx", StringComparison.Ordinal)); + => Assert.IsTrue(fileName.Equals("_MachineName_0001-01-01_00_00_00.0000000.trx", StringComparison.Ordinal)); private TrxReportEngine GenerateTrxReportEngine(int passedTestsCount, int failedTestsCount, PropertyBag propertyBag, MemoryFileStream memoryStream, bool? adapterSupportTrxCapability = null, int notExecutedTestsCount = 0, int timeoutTestsCount = 0, @@ -531,7 +578,7 @@ private TrxReportEngine GenerateTrxReportEngine(int passedTestsCount, int failed DateTime testStartTime = DateTime.Now; CancellationToken cancellationToken = CancellationToken.None; - _ = _fileSystem.Setup(x => x.Exists(It.IsAny())).Returns(false); + _ = _fileSystem.Setup(x => x.ExistFile(It.IsAny())).Returns(false); _ = _fileSystem.Setup(x => x.NewFileStream(It.IsAny(), isExplicitFileName ? FileMode.Create : FileMode.CreateNew)) .Returns(memoryStream); diff --git a/test/UnitTests/Microsoft.Testing.Extensions.VSTestBridge.UnitTests/CommandLine/RunSettingsCommandLineOptionsProviderTests.cs b/test/UnitTests/Microsoft.Testing.Extensions.VSTestBridge.UnitTests/CommandLine/RunSettingsCommandLineOptionsProviderTests.cs index 1609ade663..8788c56af4 100644 --- a/test/UnitTests/Microsoft.Testing.Extensions.VSTestBridge.UnitTests/CommandLine/RunSettingsCommandLineOptionsProviderTests.cs +++ b/test/UnitTests/Microsoft.Testing.Extensions.VSTestBridge.UnitTests/CommandLine/RunSettingsCommandLineOptionsProviderTests.cs @@ -20,7 +20,7 @@ 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); + fileSystem.Setup(fs => fs.ExistFile(It.IsAny())).Returns(false); var provider = new RunSettingsCommandLineOptionsProvider(new TestExtension(), fileSystem.Object); CommandLineOption option = provider.GetCommandLineOptions().Single(); @@ -39,7 +39,7 @@ 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.ExistFile(filePath)).Returns(true); fileSystem.Setup(fs => fs.NewFileStream(filePath, FileMode.Open, FileAccess.Read)).Throws(new IOException()); var provider = new RunSettingsCommandLineOptionsProvider(new TestExtension(), fileSystem.Object); @@ -59,7 +59,7 @@ 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.ExistFile(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); diff --git a/test/UnitTests/Microsoft.Testing.Extensions.VSTestBridge.UnitTests/Helpers/TestExtension.cs b/test/UnitTests/Microsoft.Testing.Extensions.VSTestBridge.UnitTests/Helpers/TestExtension.cs index fd087790e7..71ba44329a 100644 --- a/test/UnitTests/Microsoft.Testing.Extensions.VSTestBridge.UnitTests/Helpers/TestExtension.cs +++ b/test/UnitTests/Microsoft.Testing.Extensions.VSTestBridge.UnitTests/Helpers/TestExtension.cs @@ -5,13 +5,13 @@ namespace Microsoft.Testing.Extensions.VSTestBridge.UnitTests.Helpers; internal sealed class TestExtension : IExtension { - public string Uid { get; } = "Uid"; + public string Uid => "Uid"; - public string Version { get; } = "Version"; + public string Version => "Version"; - public string DisplayName { get; } = "DisplayName"; + public string DisplayName => "DisplayName"; - public string Description { get; } = "Description"; + public string Description => "Description"; public Task IsEnabledAsync() => Task.FromResult(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 index 09809de7f8..a619ae84ad 100644 --- a/test/UnitTests/Microsoft.Testing.Extensions.VSTestBridge.UnitTests/ObjectModel/ObjectModelConvertersTests.cs +++ b/test/UnitTests/Microsoft.Testing.Extensions.VSTestBridge.UnitTests/ObjectModel/ObjectModelConvertersTests.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. -#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. - using Microsoft.Testing.Extensions.TrxReport.Abstractions; using Microsoft.Testing.Extensions.VSTestBridge.ObjectModel; using Microsoft.Testing.Platform.Capabilities.TestFramework; @@ -23,22 +21,25 @@ public sealed class ObjectModelConvertersTests private static readonly IClientInfo ClientInfo = new ClientInfoService(WellKnownClients.VisualStudio, "1.0.0"); [TestMethod] - public void ToTestNode_WhenTestCaseHasDisplayName_TestNodeDisplayNameUsesIt() + [DataRow(true)] + [DataRow(false)] + public void ToTestNode_WhenTestCaseHasDisplayName_TestNodeDisplayNameUsesIt(bool useFullyQualifiedNameAsUid) { TestCase testCase = new("SomeFqn", new("executor://uri", UriKind.Absolute), "source.cs") { DisplayName = "MyDisplayName", }; - var testNode = testCase.ToTestNode(false, null, new ConsoleCommandLineOptions(), ClientInfo); + var testNode = testCase.ToTestNode(isTrxEnabled: false, useFullyQualifiedNameAsUid, null, new ConsoleCommandLineOptions(), ClientInfo); Assert.AreEqual("MyDisplayName", testNode.DisplayName); + Assert.AreEqual(useFullyQualifiedNameAsUid ? "SomeFqn" : testCase.Id.ToString(), testNode.Uid.Value); } [TestMethod] public void ToTestNode_WhenTestCaseHasNoDisplayName_TestNodeDisplayNameUsesIt() { TestCase testCase = new("SomeFqn", new("executor://uri", UriKind.Absolute), "source.cs"); - var testNode = testCase.ToTestNode(false, null, new ConsoleCommandLineOptions(), ClientInfo); + var testNode = testCase.ToTestNode(isTrxEnabled: false, useFullyQualifiedNameAsUid: false, null, new ConsoleCommandLineOptions(), ClientInfo); Assert.AreEqual("SomeFqn", testNode.DisplayName); } @@ -50,7 +51,7 @@ public void ToTestNode_WhenTestResultHasCodeFilePath_SetsTestFileLocationPropert { CodeFilePath = "FilePath", }); - var testNode = testResult.ToTestNode(false, null, new ConsoleCommandLineOptions(), ClientInfo); + var testNode = testResult.ToTestNode(isTrxEnabled: false, useFullyQualifiedNameAsUid: false, null, new ConsoleCommandLineOptions(), ClientInfo); Assert.AreEqual("FilePath", testNode.Properties.Single().FilePath); } @@ -63,10 +64,10 @@ public void ToTestNode_WhenTestResultOutcomeIsFailed_TestNodePropertiesContainFa ErrorMessage = "SomeErrorMessage", ErrorStackTrace = "SomeStackTrace", }; - var testNode = testResult.ToTestNode(false, null, new ConsoleCommandLineOptions(), ClientInfo); + var testNode = testResult.ToTestNode(isTrxEnabled: false, useFullyQualifiedNameAsUid: false, null, new ConsoleCommandLineOptions(), ClientInfo); - FailedTestNodeStateProperty[] failedTestNodeStateProperties = testNode.Properties.OfType().ToArray(); - Assert.AreEqual(1, failedTestNodeStateProperties.Length); + FailedTestNodeStateProperty[] failedTestNodeStateProperties = [.. testNode.Properties.OfType()]; + Assert.HasCount(1, failedTestNodeStateProperties); Assert.IsTrue(failedTestNodeStateProperties[0].Exception is VSTestException); Assert.AreEqual(testResult.ErrorStackTrace, failedTestNodeStateProperties[0].Exception!.StackTrace); Assert.AreEqual(testResult.ErrorMessage, failedTestNodeStateProperties[0].Exception!.Message); @@ -76,13 +77,15 @@ public void ToTestNode_WhenTestResultOutcomeIsFailed_TestNodePropertiesContainFa 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)); +#pragma warning disable CS0618 // Type or member is obsolete + var testCategoryProperty = TestProperty.Register("MSTestDiscoverer.TestCategory", "Label", typeof(string[]), TestPropertyAttributes.Trait, typeof(TestCase)); +#pragma warning restore CS0618 // Type or member is obsolete testResult.SetPropertyValue(testCategoryProperty, ["category1"]); - var testNode = testResult.ToTestNode(false, new NamedFeatureCapabilityWithVSTestProvider(), new ServerModeCommandLineOptions(), ClientInfo); + var testNode = testResult.ToTestNode(isTrxEnabled: false, useFullyQualifiedNameAsUid: false, new NamedFeatureCapabilityWithVSTestProvider(), new ServerModeCommandLineOptions(), ClientInfo); - TestMetadataProperty[] testMetadatas = testNode.Properties.OfType().ToArray(); - Assert.AreEqual(1, testMetadatas.Length); + TestMetadataProperty[] testMetadatas = [.. testNode.Properties.OfType()]; + Assert.HasCount(1, testMetadatas); Assert.AreEqual("category1", testMetadatas[0].Key); Assert.AreEqual(string.Empty, testMetadatas[0].Value); } @@ -91,14 +94,16 @@ public void ToTestNode_WhenTestResultHasMSTestDiscovererTestCategoryTestProperty 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)); +#pragma warning disable CS0618 // Type or member is obsolete + var testCategoryProperty = TestProperty.Register("MSTestDiscoverer.TestCategory", "Label", typeof(string[]), TestPropertyAttributes.Trait, typeof(TestCase)); +#pragma warning restore CS0618 // Type or member is obsolete testResult.SetPropertyValue(testCategoryProperty, ["category1"]); - var testNode = testResult.ToTestNode(true, new NamedFeatureCapabilityWithVSTestProvider(), new ServerModeCommandLineOptions(), ClientInfo); + var testNode = testResult.ToTestNode(isTrxEnabled: true, useFullyQualifiedNameAsUid: false, new NamedFeatureCapabilityWithVSTestProvider(), new ServerModeCommandLineOptions(), ClientInfo); - TrxCategoriesProperty[] trxCategoriesProperty = testNode.Properties.OfType().ToArray(); - Assert.AreEqual(1, trxCategoriesProperty.Length); - Assert.AreEqual(1, trxCategoriesProperty[0].Categories.Length); + TrxCategoriesProperty[] trxCategoriesProperty = [.. testNode.Properties.OfType()]; + Assert.HasCount(1, trxCategoriesProperty); + Assert.HasCount(1, trxCategoriesProperty[0].Categories); Assert.AreEqual("category1", trxCategoriesProperty[0].Categories[0]); } @@ -111,10 +116,10 @@ public void ToTestNode_WhenTestCaseHasOriginalExecutorUriProperty_TestNodeProper testCase.SetPropertyValue(originalExecutorUriProperty, new Uri("https://vs.com/")); - var testNode = testCase.ToTestNode(false, new NamedFeatureCapabilityWithVSTestProvider(), new ServerModeCommandLineOptions(), ClientInfo); + var testNode = testCase.ToTestNode(isTrxEnabled: false, useFullyQualifiedNameAsUid: false, new NamedFeatureCapabilityWithVSTestProvider(), new ServerModeCommandLineOptions(), ClientInfo); - SerializableKeyValuePairStringProperty[] serializableKeyValuePairStringProperty = testNode.Properties.OfType().ToArray(); - Assert.AreEqual(3, serializableKeyValuePairStringProperty.Length); + SerializableKeyValuePairStringProperty[] serializableKeyValuePairStringProperty = [.. testNode.Properties.OfType()]; + Assert.HasCount(3, serializableKeyValuePairStringProperty); Assert.AreEqual(VSTestTestNodeProperties.OriginalExecutorUriPropertyName, serializableKeyValuePairStringProperty[0].Key); Assert.AreEqual("https://vs.com/", serializableKeyValuePairStringProperty[0].Value); } @@ -124,9 +129,9 @@ public void ToTestNode_WhenTestResultHasFullyQualifiedTypeAndTrxEnabled_TestNode { TestResult testResult = new(new TestCase("assembly.class.test", new("executor://uri", UriKind.Absolute), "source.cs")); - var testNode = testResult.ToTestNode(true, null, new ConsoleCommandLineOptions(), ClientInfo); + var testNode = testResult.ToTestNode(isTrxEnabled: true, useFullyQualifiedNameAsUid: false, null, new ConsoleCommandLineOptions(), ClientInfo); - Assert.AreEqual(1, testNode.Properties.OfType()?.Length); + Assert.AreEqual(0, testNode.Properties.OfType()?.Length); Assert.AreEqual("assembly.class", testNode.Properties.Single().FullyQualifiedTypeName); } @@ -135,9 +140,9 @@ public void ToTestNode_WhenTestResultHasNoFullyQualifiedTypeAndTrxEnabled_Throws { TestResult testResult = new(new TestCase("test", new("executor://uri", UriKind.Absolute), "source.cs")); - string errorMessage = Assert.ThrowsException(() => testResult.ToTestNode(true, null, new ConsoleCommandLineOptions(), ClientInfo)).Message; + string errorMessage = Assert.ThrowsExactly(() => testResult.ToTestNode(isTrxEnabled: true, useFullyQualifiedNameAsUid: false, null, new ConsoleCommandLineOptions(), ClientInfo)).Message; - Assert.IsTrue(errorMessage.Contains("Unable to parse fully qualified type name from test case: ")); + Assert.Contains("Unable to parse fully qualified type name from test case: ", errorMessage); } [TestMethod] @@ -153,7 +158,7 @@ public void ToTestNode_FromTestResult_TestNodePropertiesContainCorrectTimingProp EndTime = endTime, Duration = duration, }; - var testNode = testResult.ToTestNode(false, null, new ConsoleCommandLineOptions(), ClientInfo); + var testNode = testResult.ToTestNode(isTrxEnabled: false, useFullyQualifiedNameAsUid: false, null, new ConsoleCommandLineOptions(), ClientInfo); var testResultTimingProperty = new TimingProperty(new(startTime, endTime, duration), []); Assert.AreEqual(testNode.Properties.OfType()[0], testResultTimingProperty); @@ -167,13 +172,13 @@ public void ToTestNode_WhenTestResultOutcomeIsNotFoundWithoutSetErrorMessage_Tes Outcome = TestOutcome.NotFound, ErrorStackTrace = "SomeStackTrace", }; - var testNode = testResult.ToTestNode(false, null, new ConsoleCommandLineOptions(), ClientInfo); + var testNode = testResult.ToTestNode(isTrxEnabled: false, useFullyQualifiedNameAsUid: false, null, new ConsoleCommandLineOptions(), ClientInfo); - ErrorTestNodeStateProperty[] errorTestNodeStateProperties = testNode.Properties.OfType().ToArray(); - Assert.AreEqual(1, errorTestNodeStateProperties.Length); + ErrorTestNodeStateProperty[] errorTestNodeStateProperties = [.. testNode.Properties.OfType()]; + Assert.HasCount(1, errorTestNodeStateProperties); Assert.IsTrue(errorTestNodeStateProperties[0].Exception is VSTestException); Assert.AreEqual(testResult.ErrorStackTrace, errorTestNodeStateProperties[0].Exception!.StackTrace); - Assert.IsTrue(errorTestNodeStateProperties[0].Exception!.Message.Contains("Not found")); + Assert.Contains("Not found", errorTestNodeStateProperties[0].Exception!.Message); } [TestMethod] @@ -183,10 +188,10 @@ public void ToTestNode_WhenTestResultOutcomeIsSkipped_TestNodePropertiesContainS { Outcome = TestOutcome.Skipped, }; - var testNode = testResult.ToTestNode(false, null, new ConsoleCommandLineOptions(), ClientInfo); + var testNode = testResult.ToTestNode(isTrxEnabled: false, useFullyQualifiedNameAsUid: false, null, new ConsoleCommandLineOptions(), ClientInfo); - SkippedTestNodeStateProperty[] skipTestNodeStateProperties = testNode.Properties.OfType().ToArray(); - Assert.AreEqual(1, skipTestNodeStateProperties.Length); + SkippedTestNodeStateProperty[] skipTestNodeStateProperties = [.. testNode.Properties.OfType()]; + Assert.HasCount(1, skipTestNodeStateProperties); } [TestMethod] @@ -196,10 +201,10 @@ public void ToTestNode_WhenTestResultOutcomeIsNone_TestNodePropertiesContainSkip { Outcome = TestOutcome.None, }; - var testNode = testResult.ToTestNode(false, null, new ConsoleCommandLineOptions(), ClientInfo); + var testNode = testResult.ToTestNode(isTrxEnabled: false, useFullyQualifiedNameAsUid: false, null, new ConsoleCommandLineOptions(), ClientInfo); - SkippedTestNodeStateProperty[] skipTestNodeStateProperties = testNode.Properties.OfType().ToArray(); - Assert.AreEqual(1, skipTestNodeStateProperties.Length); + SkippedTestNodeStateProperty[] skipTestNodeStateProperties = [.. testNode.Properties.OfType()]; + Assert.HasCount(1, skipTestNodeStateProperties); } [TestMethod] @@ -209,10 +214,10 @@ public void ToTestNode_WhenTestResultOutcomeIsPassed_TestNodePropertiesContainPa { Outcome = TestOutcome.Passed, }; - var testNode = testResult.ToTestNode(false, null, new ConsoleCommandLineOptions(), ClientInfo); + var testNode = testResult.ToTestNode(isTrxEnabled: false, useFullyQualifiedNameAsUid: false, null, new ConsoleCommandLineOptions(), ClientInfo); - PassedTestNodeStateProperty[] passedTestNodeStateProperties = testNode.Properties.OfType().ToArray(); - Assert.AreEqual(1, passedTestNodeStateProperties.Length); + PassedTestNodeStateProperty[] passedTestNodeStateProperties = [.. testNode.Properties.OfType()]; + Assert.HasCount(1, passedTestNodeStateProperties); } [TestMethod] @@ -220,10 +225,10 @@ public void ToTestNode_WhenTestCaseHasUidAndDisplayNameWithWellKnownClient_TestN { var testCase = new TestCase("SomeFqn", new("executor://uri", UriKind.Absolute), "source.cs"); - var testNode = testCase.ToTestNode(false, new NamedFeatureCapabilityWithVSTestProvider(), new ServerModeCommandLineOptions(), ClientInfo); + var testNode = testCase.ToTestNode(isTrxEnabled: false, useFullyQualifiedNameAsUid: false, new NamedFeatureCapabilityWithVSTestProvider(), new ServerModeCommandLineOptions(), ClientInfo); - SerializableKeyValuePairStringProperty[] errorTestNodeStateProperties = testNode.Properties.OfType().ToArray(); - Assert.AreEqual(2, errorTestNodeStateProperties.Length, "Expected 2 SerializableKeyValuePairStringProperty"); + SerializableKeyValuePairStringProperty[] errorTestNodeStateProperties = [.. testNode.Properties.OfType()]; + Assert.HasCount(2, errorTestNodeStateProperties); Assert.AreEqual("vstest.TestCase.FullyQualifiedName", errorTestNodeStateProperties[0].Key); Assert.AreEqual("SomeFqn", errorTestNodeStateProperties[0].Value); Assert.AreEqual("vstest.TestCase.Id", errorTestNodeStateProperties[1].Key); @@ -238,10 +243,10 @@ public void ToTestNode_WhenTestResultHasTraits_TestNodePropertiesContainIt() Traits = { new Trait("key", "value") }, }; - var testNode = testResult.ToTestNode(false, new NamedFeatureCapabilityWithVSTestProvider(), new ServerModeCommandLineOptions(), ClientInfo); + var testNode = testResult.ToTestNode(isTrxEnabled: false, useFullyQualifiedNameAsUid: false, new NamedFeatureCapabilityWithVSTestProvider(), new ServerModeCommandLineOptions(), ClientInfo); - TestMetadataProperty[] testMetadatas = testNode.Properties.OfType().ToArray(); - Assert.AreEqual(1, testMetadatas.Length); + TestMetadataProperty[] testMetadatas = [.. testNode.Properties.OfType()]; + Assert.HasCount(1, testMetadatas); Assert.AreEqual("key", testMetadatas[0].Key); Assert.AreEqual("value", testMetadatas[0].Value); } @@ -259,10 +264,10 @@ public void ToTestNode_WhenTestResultHasMultipleStandardOutputMessages_TestNodeP }, }; - var testNode = testResult.ToTestNode(false, new NamedFeatureCapabilityWithVSTestProvider(), new ServerModeCommandLineOptions(), ClientInfo); + var testNode = testResult.ToTestNode(isTrxEnabled: false, useFullyQualifiedNameAsUid: false, new NamedFeatureCapabilityWithVSTestProvider(), new ServerModeCommandLineOptions(), ClientInfo); - StandardOutputProperty[] standardOutputProperties = testNode.Properties.OfType().ToArray(); - Assert.IsTrue(standardOutputProperties.Length == 1); + StandardOutputProperty[] standardOutputProperties = [.. testNode.Properties.OfType()]; + Assert.HasCount(1, standardOutputProperties); Assert.AreEqual($"message1{Environment.NewLine}message2", standardOutputProperties[0].StandardOutput); } @@ -279,10 +284,10 @@ public void ToTestNode_WhenTestResultHasMultipleStandardErrorMessages_TestNodePr }, }; - var testNode = testResult.ToTestNode(false, new NamedFeatureCapabilityWithVSTestProvider(), new ServerModeCommandLineOptions(), ClientInfo); + var testNode = testResult.ToTestNode(isTrxEnabled: false, useFullyQualifiedNameAsUid: false, new NamedFeatureCapabilityWithVSTestProvider(), new ServerModeCommandLineOptions(), ClientInfo); - StandardErrorProperty[] standardErrorProperties = testNode.Properties.OfType().ToArray(); - Assert.IsTrue(standardErrorProperties.Length == 1); + StandardErrorProperty[] standardErrorProperties = [.. testNode.Properties.OfType()]; + Assert.HasCount(1, standardErrorProperties); Assert.AreEqual($"message1{Environment.NewLine}message2", standardErrorProperties[0].StandardError); } diff --git a/test/UnitTests/Microsoft.Testing.Extensions.VSTestBridge.UnitTests/ObjectModel/RunSettingsPatcherTests.cs b/test/UnitTests/Microsoft.Testing.Extensions.VSTestBridge.UnitTests/ObjectModel/RunSettingsPatcherTests.cs index 92c52dbde1..262f7bc9ae 100644 --- a/test/UnitTests/Microsoft.Testing.Extensions.VSTestBridge.UnitTests/ObjectModel/RunSettingsPatcherTests.cs +++ b/test/UnitTests/Microsoft.Testing.Extensions.VSTestBridge.UnitTests/ObjectModel/RunSettingsPatcherTests.cs @@ -94,7 +94,7 @@ public void Patch_WhenRunSettingsExists_MergesParameters() XDocument runSettingsDocument = RunSettingsPatcher.Patch(runSettings, _configuration.Object, new ClientInfoService(string.Empty, string.Empty), _commandLineOptions.Object); - XElement[] testRunParameters = runSettingsDocument.XPathSelectElements("RunSettings/TestRunParameters/Parameter").ToArray(); + XElement[] testRunParameters = [.. runSettingsDocument.XPathSelectElements("RunSettings/TestRunParameters/Parameter")]; Assert.AreEqual("key1", testRunParameters[0].Attribute("name")!.Value); Assert.AreEqual("value1", testRunParameters[0].Attribute("value")!.Value); Assert.AreEqual("key2", testRunParameters[1].Attribute("name")!.Value); @@ -118,7 +118,7 @@ public void Patch_WhenRunSettingsDoesNotExist_AddParameters() XDocument runSettingsDocument = RunSettingsPatcher.Patch(null, _configuration.Object, new ClientInfoService(string.Empty, string.Empty), _commandLineOptions.Object); - XElement[] testRunParameters = runSettingsDocument.XPathSelectElements("RunSettings/TestRunParameters/Parameter").ToArray(); + XElement[] testRunParameters = [.. runSettingsDocument.XPathSelectElements("RunSettings/TestRunParameters/Parameter")]; Assert.AreEqual("key1", testRunParameters[0].Attribute("name")!.Value); Assert.AreEqual("value1", testRunParameters[0].Attribute("value")!.Value); Assert.AreEqual("key2", testRunParameters[1].Attribute("name")!.Value); diff --git a/test/UnitTests/Microsoft.Testing.Platform.MSBuild.UnitTests/MSBuildTests.cs b/test/UnitTests/Microsoft.Testing.Platform.MSBuild.UnitTests/MSBuildTests.cs index ad5f780f08..e9762332bd 100644 --- a/test/UnitTests/Microsoft.Testing.Platform.MSBuild.UnitTests/MSBuildTests.cs +++ b/test/UnitTests/Microsoft.Testing.Platform.MSBuild.UnitTests/MSBuildTests.cs @@ -10,15 +10,11 @@ namespace Microsoft.Testing.Platform.MSBuild.UnitTests; [TestClass] public sealed class MSBuildTests { - private readonly Mock _buildEngine; - private readonly List _errors; + private readonly Mock _buildEngine = new(); + private readonly List _errors = []; - public MSBuildTests() - { - _buildEngine = new Mock(); - _errors = new List(); + public MSBuildTests() => _buildEngine.Setup(x => x.LogErrorEvent(It.IsAny())).Callback(e => _errors.Add(e)); - } [TestMethod] public void Verify_Correct_Registration_Order_For_WellKnown_Extensions() @@ -65,7 +61,7 @@ internal sealed class MicrosoftTestingPlatformEntryPoint private sealed class InMemoryFileSystem : IFileSystem { - public Dictionary Files { get; } = new(); + public Dictionary Files { get; } = []; public void CopyFile(string source, string destination) => throw new NotImplementedException(); @@ -80,7 +76,7 @@ private sealed class InMemoryFileSystem : IFileSystem private sealed class CustomTaskItem : ITaskItem { - private readonly Dictionary _keyValuePairs = new(); + private readonly Dictionary _keyValuePairs = []; public CustomTaskItem(string itemSpec) => ItemSpec = itemSpec; diff --git a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/CommandLine/ArgumentArityTests.cs b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/CommandLine/ArgumentArityTests.cs index 5cada34ede..c688a2062f 100644 --- a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/CommandLine/ArgumentArityTests.cs +++ b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/CommandLine/ArgumentArityTests.cs @@ -121,16 +121,16 @@ public async Task ParseAndValidate_WhenOptionsGetsTheExpectedNumberOfArguments_R private sealed class ExtensionCommandLineProviderMockOptionsWithDifferentArity : ICommandLineOptionsProvider { - public string Uid { get; } = nameof(PlatformCommandLineProvider); + public string Uid => nameof(PlatformCommandLineProvider); /// - public string Version { get; } = AppVersion.DefaultSemVer; + public string Version => AppVersion.DefaultSemVer; /// - public string DisplayName { get; } = "Microsoft Testing Platform command line provider"; + public string DisplayName => "Microsoft Testing Platform command line provider"; /// - public string Description { get; } = "Built-in command line provider"; + public string Description => "Built-in command line provider"; /// public Task IsEnabledAsync() => Task.FromResult(true); diff --git a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/CommandLine/CommandLineHandlerTests.cs b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/CommandLine/CommandLineHandlerTests.cs index 50462c0cb1..4070f86876 100644 --- a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/CommandLine/CommandLineHandlerTests.cs +++ b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/CommandLine/CommandLineHandlerTests.cs @@ -38,8 +38,8 @@ public async Task ParseAndValidateAsync_InvalidCommandLineArguments_ReturnsFalse // Assert Assert.IsFalse(result.IsValid); - StringAssert.Contains(result.ErrorMessage, "Invalid command line arguments:"); - StringAssert.Contains(result.ErrorMessage, "Unexpected argument 'a'"); + Assert.Contains("Invalid command line arguments:", result.ErrorMessage); + Assert.Contains("Unexpected argument 'a'", result.ErrorMessage); } [TestMethod] @@ -75,7 +75,7 @@ public async Task ParseAndValidateAsync_DuplicateOption_ReturnsFalse() // Assert Assert.IsFalse(result.IsValid); - StringAssert.Contains(result.ErrorMessage, "Option '--userOption' is declared by multiple extensions: 'Microsoft Testing Platform command line provider', 'Microsoft Testing Platform command line provider'"); + Assert.Contains("Option '--userOption' is declared by multiple extensions: 'Microsoft Testing Platform command line provider', 'Microsoft Testing Platform command line provider'", result.ErrorMessage); } [TestMethod] @@ -275,8 +275,8 @@ public void GetOptionValue_OptionDoesNotExist_ReturnsNull() _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")); + Assert.Contains("Invalid command line arguments:", ((TextOutputDeviceData)data).Text); + Assert.Contains("Unexpected argument", ((TextOutputDeviceData)data).Text); }); CommandLineHandler commandLineHandler = new(parseResult, _extensionCommandLineOptionsProviders, _systemCommandLineOptionsProviders, @@ -294,16 +294,16 @@ private sealed class ExtensionCommandLineProviderMockReservedOptions : ICommandL { public const string HelpOption = "help"; - public string Uid { get; } = nameof(PlatformCommandLineProvider); + public string Uid => nameof(PlatformCommandLineProvider); /// - public string Version { get; } = AppVersion.DefaultSemVer; + public string Version => AppVersion.DefaultSemVer; /// - public string DisplayName { get; } = "Microsoft Testing Platform command line provider"; + public string DisplayName => "Microsoft Testing Platform command line provider"; /// - public string Description { get; } = "Built-in command line provider"; + public string Description => "Built-in command line provider"; /// public Task IsEnabledAsync() => Task.FromResult(true); @@ -322,16 +322,16 @@ private sealed class ExtensionCommandLineProviderMockUnknownOption : ICommandLin { public const string Option = "option"; - public string Uid { get; } = nameof(PlatformCommandLineProvider); + public string Uid => nameof(PlatformCommandLineProvider); /// - public string Version { get; } = AppVersion.DefaultSemVer; + public string Version => AppVersion.DefaultSemVer; /// - public string DisplayName { get; } = "Microsoft Testing Platform command line provider"; + public string DisplayName => "Microsoft Testing Platform command line provider"; /// - public string Description { get; } = "Built-in command line provider"; + public string Description => "Built-in command line provider"; /// public Task IsEnabledAsync() => Task.FromResult(true); @@ -352,16 +352,16 @@ private sealed class ExtensionCommandLineProviderMockInvalidConfiguration : ICom public ExtensionCommandLineProviderMockInvalidConfiguration(string optionName = "option") => _option = optionName; - public string Uid { get; } = nameof(PlatformCommandLineProvider); + public string Uid => nameof(PlatformCommandLineProvider); /// - public string Version { get; } = AppVersion.DefaultSemVer; + public string Version => AppVersion.DefaultSemVer; /// - public string DisplayName { get; } = "Microsoft Testing Platform command line provider"; + public string DisplayName => "Microsoft Testing Platform command line provider"; /// - public string Description { get; } = "Built-in command line provider"; + public string Description => "Built-in command line provider"; /// public Task IsEnabledAsync() => Task.FromResult(true); diff --git a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/CommandLine/PlatformCommandLineProviderTests.cs b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/CommandLine/PlatformCommandLineProviderTests.cs index 4ea02fad22..f838ea4e3c 100644 --- a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/CommandLine/PlatformCommandLineProviderTests.cs +++ b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/CommandLine/PlatformCommandLineProviderTests.cs @@ -181,4 +181,77 @@ public async Task IsNotValid_If_ExitOnProcess_Not_Running() Assert.IsFalse(validateOptionsResult.IsValid); Assert.IsTrue(validateOptionsResult.ErrorMessage.StartsWith($"Invalid PID '{pid}'", StringComparison.OrdinalIgnoreCase)); } + + [TestMethod] + [DataRow("1.5s")] + [DataRow("2.0m")] + [DataRow("0.5h")] + [DataRow("10s")] + [DataRow("30m")] + [DataRow("1h")] + public async Task IsValid_If_Timeout_Has_CorrectFormat_InvariantCulture(string timeout) + { + var provider = new PlatformCommandLineProvider(); + CommandLineOption option = provider.GetCommandLineOptions().First(x => x.Name == PlatformCommandLineProvider.TimeoutOptionKey); + + // Save current culture + CultureInfo originalCulture = CultureInfo.CurrentCulture; + try + { + // Test with various cultures to ensure invariant parsing works + foreach (string cultureName in new[] { "en-US", "de-DE", "fr-FR" }) + { + CultureInfo.CurrentCulture = CultureInfo.GetCultureInfo(cultureName); + ValidationResult validateOptionsResult = await provider.ValidateOptionArgumentsAsync(option, [timeout]); + Assert.IsTrue(validateOptionsResult.IsValid, $"Failed with culture {cultureName} and timeout {timeout}"); + Assert.IsTrue(string.IsNullOrEmpty(validateOptionsResult.ErrorMessage)); + } + } + finally + { + // Restore original culture + CultureInfo.CurrentCulture = originalCulture; + } + } + + [TestMethod] + [DataRow("1,5s")] // German decimal separator + [DataRow("invalid")] + [DataRow("1.5")] // Missing unit + [DataRow("abc.5s")] + public async Task IsInvalid_If_Timeout_Has_IncorrectFormat(string timeout) + { + var provider = new PlatformCommandLineProvider(); + CommandLineOption option = provider.GetCommandLineOptions().First(x => x.Name == PlatformCommandLineProvider.TimeoutOptionKey); + + ValidationResult validateOptionsResult = await provider.ValidateOptionArgumentsAsync(option, [timeout]); + Assert.IsFalse(validateOptionsResult.IsValid); + Assert.AreEqual(PlatformResources.PlatformCommandLineTimeoutArgumentErrorMessage, validateOptionsResult.ErrorMessage); + } + + [TestMethod] + public async Task Timeout_Parsing_Uses_InvariantCulture_NotCurrentCulture() + { + var provider = new PlatformCommandLineProvider(); + CommandLineOption option = provider.GetCommandLineOptions().First(x => x.Name == PlatformCommandLineProvider.TimeoutOptionKey); + + // Save current culture + CultureInfo originalCulture = CultureInfo.CurrentCulture; + try + { + // Set culture to German where decimal separator is comma + CultureInfo.CurrentCulture = CultureInfo.GetCultureInfo("de-DE"); + // This should work because we use invariant culture (period as decimal separator) + ValidationResult validResult = await provider.ValidateOptionArgumentsAsync(option, ["1.5s"]); + Assert.IsTrue(validResult.IsValid, "1.5s should be valid when using invariant culture"); + // This should fail because comma is not valid in invariant culture + ValidationResult invalidResult = await provider.ValidateOptionArgumentsAsync(option, ["1,5s"]); + Assert.IsFalse(invalidResult.IsValid, "1,5s should be invalid when using invariant culture"); + } + finally + { + // Restore original culture + CultureInfo.CurrentCulture = originalCulture; + } + } } diff --git a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Configuration/ConfigurationExtensionsTests.cs b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Configuration/ConfigurationExtensionsTests.cs index 49d2df67cb..104bff4626 100644 --- a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Configuration/ConfigurationExtensionsTests.cs +++ b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Configuration/ConfigurationExtensionsTests.cs @@ -42,6 +42,6 @@ public void ConfigurationExtensions_TestedMethod_ThrowsArgumentNullException(str .Setup(configuration => configuration[key]) .Returns(value: null); - Assert.ThrowsException(() => GetActualValueFromConfiguration(configuration.Object, key)); + Assert.ThrowsExactly(() => GetActualValueFromConfiguration(configuration.Object, key)); } } diff --git a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Configuration/ConfigurationManagerTests.cs b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Configuration/ConfigurationManagerTests.cs index e1b6c394d2..063e9b5116 100644 --- a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Configuration/ConfigurationManagerTests.cs +++ b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Configuration/ConfigurationManagerTests.cs @@ -18,26 +18,18 @@ namespace Microsoft.Testing.Platform.UnitTests; [TestClass] public sealed class ConfigurationManagerTests { - private readonly ServiceProvider _serviceProvider; - - public ConfigurationManagerTests() - { - _serviceProvider = new(); - _serviceProvider.AddService(new SystemFileSystem()); - } - [TestMethod] [DynamicData(nameof(GetConfigurationValueFromJsonData))] public async ValueTask GetConfigurationValueFromJson(string jsonFileConfig, string key, string? result) { Mock fileSystem = new(); - fileSystem.Setup(x => x.Exists(It.IsAny())).Returns(true); + fileSystem.Setup(x => x.ExistFile(It.IsAny())).Returns(true); fileSystem.Setup(x => x.NewFileStream(It.IsAny(), FileMode.Open, FileAccess.Read)) .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)); - IConfiguration configuration = await configurationManager.BuildAsync(null, new CommandLineParseResult(null, new List(), Array.Empty())); + IConfiguration configuration = await configurationManager.BuildAsync(null, new CommandLineParseResult(null, new List(), [])); Assert.AreEqual(result, configuration[key], $"Expected '{result}' found '{configuration[key]}'"); } @@ -62,7 +54,7 @@ public async ValueTask GetConfigurationValueFromJson(string jsonFileConfig, stri public async ValueTask InvalidJson_Fail() { Mock fileSystem = new(); - fileSystem.Setup(x => x.Exists(It.IsAny())).Returns(true); + fileSystem.Setup(x => x.ExistFile(It.IsAny())).Returns(true); fileSystem.Setup(x => x.NewFileStream(It.IsAny(), FileMode.Open, FileAccess.Read)).Returns(() => new MemoryFileStream(Encoding.UTF8.GetBytes(string.Empty))); CurrentTestApplicationModuleInfo testApplicationModuleInfo = new(new SystemEnvironment(), new SystemProcessHandler()); ConfigurationManager configurationManager = new(fileSystem.Object, testApplicationModuleInfo); @@ -71,9 +63,9 @@ public async ValueTask InvalidJson_Fail() // The behavior difference is System.Text.Json vs Jsonite #if NETFRAMEWORK - await Assert.ThrowsAsync(() => configurationManager.BuildAsync(null, new CommandLineParseResult(null, new List(), Array.Empty())), ex => ex?.ToString() ?? "No exception was thrown"); + await Assert.ThrowsAsync(() => configurationManager.BuildAsync(null, new CommandLineParseResult(null, new List(), [])), ex => ex?.ToString() ?? "No exception was thrown"); #else - await Assert.ThrowsAsync(() => configurationManager.BuildAsync(null, new CommandLineParseResult(null, new List(), Array.Empty())), ex => ex?.ToString() ?? "No exception was thrown"); + await Assert.ThrowsAsync(() => configurationManager.BuildAsync(null, new CommandLineParseResult(null, new List(), [])), ex => ex?.ToString() ?? "No exception was thrown"); #endif } @@ -84,7 +76,7 @@ public async ValueTask GetConfigurationValueFromJsonWithFileLoggerProvider(strin byte[] bytes = Encoding.UTF8.GetBytes(jsonFileConfig); Mock fileSystem = new(); - fileSystem.Setup(x => x.Exists(It.IsAny())).Returns(true); + fileSystem.Setup(x => x.ExistFile(It.IsAny())).Returns(true); fileSystem.Setup(x => x.NewFileStream(It.IsAny(), FileMode.Open, FileAccess.Read)) .Returns(() => new MemoryFileStream(bytes)); @@ -99,7 +91,7 @@ public async ValueTask GetConfigurationValueFromJsonWithFileLoggerProvider(strin configurationManager.AddConfigurationSource(() => new JsonConfigurationSource(testApplicationModuleInfo, fileSystem.Object, null)); - IConfiguration configuration = await configurationManager.BuildAsync(loggerProviderMock.Object, new CommandLineParseResult(null, new List(), Array.Empty())); + IConfiguration configuration = await configurationManager.BuildAsync(loggerProviderMock.Object, new CommandLineParseResult(null, new List(), [])); Assert.AreEqual(result, configuration[key], $"Expected '{result}' found '{configuration[key]}'"); loggerMock.Verify(x => x.LogAsync(LogLevel.Trace, It.IsAny(), null, LoggingExtensions.Formatter), Times.Once); @@ -110,7 +102,7 @@ public async ValueTask BuildAsync_EmptyConfigurationSources_ThrowsException() { CurrentTestApplicationModuleInfo testApplicationModuleInfo = new(new SystemEnvironment(), new SystemProcessHandler()); ConfigurationManager configurationManager = new(new SystemFileSystem(), testApplicationModuleInfo); - await Assert.ThrowsAsync(() => configurationManager.BuildAsync(null, new CommandLineParseResult(null, new List(), Array.Empty()))); + await Assert.ThrowsAsync(() => configurationManager.BuildAsync(null, new CommandLineParseResult(null, new List(), []))); } [TestMethod] @@ -123,7 +115,7 @@ public async ValueTask BuildAsync_ConfigurationSourcesNotEnabledAsync_ThrowsExce ConfigurationManager configurationManager = new(new SystemFileSystem(), testApplicationModuleInfo); configurationManager.AddConfigurationSource(() => mockConfigurationSource.Object); - await Assert.ThrowsAsync(() => configurationManager.BuildAsync(null, new CommandLineParseResult(null, new List(), Array.Empty()))); + await Assert.ThrowsAsync(() => configurationManager.BuildAsync(null, new CommandLineParseResult(null, new List(), []))); mockConfigurationSource.Verify(x => x.IsEnabledAsync(), Times.Once); } @@ -143,7 +135,7 @@ public async ValueTask BuildAsync_ConfigurationSourceIsAsyncInitializableExtensi ConfigurationManager configurationManager = new(new SystemFileSystem(), testApplicationModuleInfo); configurationManager.AddConfigurationSource(() => fakeConfigurationSource); - await Assert.ThrowsAsync(() => configurationManager.BuildAsync(null, new CommandLineParseResult(null, new List(), Array.Empty()))); + await Assert.ThrowsAsync(() => configurationManager.BuildAsync(null, new CommandLineParseResult(null, new List(), []))); } private class FakeConfigurationSource : IConfigurationSource, IAsyncInitializableExtension diff --git a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Helpers/CountDownEventTests.cs b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Helpers/CountDownEventTests.cs index ee938ad28a..49f81499da 100644 --- a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Helpers/CountDownEventTests.cs +++ b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Helpers/CountDownEventTests.cs @@ -8,6 +8,8 @@ namespace Microsoft.Testing.Platform.UnitTests; [TestClass] public sealed class CountDownEventTests { + public TestContext TestContext { get; set; } + [TestMethod] public async Task CountDownEvent_WaitAsync_Succeeded() { @@ -15,11 +17,11 @@ public async Task CountDownEvent_WaitAsync_Succeeded() Task waiter1 = Task.Run(() => countdownEvent.WaitAsync(CancellationToken.None)); Task waiter2 = Task.Run(() => countdownEvent.WaitAsync(CancellationToken.None)); - await Task.Delay(500); + await Task.Delay(500, TestContext.CancellationToken); countdownEvent.Signal(); - await Task.Delay(500); + await Task.Delay(500, TestContext.CancellationToken); countdownEvent.Signal(); - await Task.Delay(500); + await Task.Delay(500, TestContext.CancellationToken); countdownEvent.Signal(); await waiter1.TimeoutAfterAsync(TimeSpan.FromSeconds(30)); diff --git a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Helpers/SystemAsyncMonitorTests.cs b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Helpers/SystemAsyncMonitorTests.cs index d6d0daaa03..c8718a391e 100644 --- a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Helpers/SystemAsyncMonitorTests.cs +++ b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Helpers/SystemAsyncMonitorTests.cs @@ -8,6 +8,8 @@ namespace Microsoft.Testing.Platform.UnitTests; [TestClass] public sealed class SystemAsyncMonitorTests { + public TestContext TestContext { get; set; } + [TestMethod] public async Task AsyncMonitor_ShouldCorrectlyLock() { @@ -17,15 +19,15 @@ 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, TestContext.CancellationToken)); } - await Task.WhenAll(tasks.ToArray()); + await Task.WhenAll([.. tasks]); // Give more time to be above 3s Thread.Sleep(500); - Assert.IsTrue(stopwatch.ElapsedMilliseconds > 3000); + Assert.IsGreaterThan(3000, stopwatch.ElapsedMilliseconds); async Task TestLock() { @@ -37,7 +39,7 @@ async Task TestLock() } lockState = true; - await Task.Delay(1000); + await Task.Delay(1000, TestContext.CancellationToken); lockState = false; } } diff --git a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Helpers/TaskExtensionsTests.cs b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Helpers/TaskExtensionsTests.cs index 1a020ae938..d418c53c16 100644 --- a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Helpers/TaskExtensionsTests.cs +++ b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Helpers/TaskExtensionsTests.cs @@ -8,22 +8,24 @@ namespace Microsoft.Testing.Platform.UnitTests; [TestClass] public sealed class TaskExtensionsTests { + public TestContext TestContext { get; set; } + [TestMethod] public async Task TimeoutAfterAsync_Succeeds() => await Assert.ThrowsAsync(async () => - await Task.Delay(TimeSpan.FromSeconds(60)).TimeoutAfterAsync(TimeSpan.FromSeconds(2))); + await Task.Delay(TimeSpan.FromSeconds(60), TestContext.CancellationToken).TimeoutAfterAsync(TimeSpan.FromSeconds(2))); [TestMethod] public async Task TimeoutAfterAsync_CancellationToken_Succeeds() => await Assert.ThrowsAsync(async () => - await Task.Delay(TimeSpan.FromSeconds(60)).TimeoutAfterAsync( + await Task.Delay(TimeSpan.FromSeconds(60), TestContext.CancellationToken).TimeoutAfterAsync( TimeSpan.FromSeconds(30), new CancellationTokenSource(TimeSpan.FromSeconds(2)).Token)); [TestMethod] public async Task TimeoutAfterAsync_CancellationTokenNone_Succeeds() => await Assert.ThrowsAsync(async () => - await Task.Delay(TimeSpan.FromSeconds(60)).TimeoutAfterAsync( + await Task.Delay(TimeSpan.FromSeconds(60), TestContext.CancellationToken).TimeoutAfterAsync( TimeSpan.FromSeconds(2), CancellationToken.None)); @@ -75,16 +77,17 @@ public async Task CancellationAsync_ObserveException_Succeeds() { ManualResetEvent waitException = new(false); await Assert.ThrowsAsync(async () - => await Task.Run(async () => - { - await Task.Delay(TimeSpan.FromSeconds(10)); - waitException.Set(); - throw new InvalidOperationException(); - }).WithCancellationAsync(new CancellationTokenSource(TimeSpan.FromSeconds(1)).Token)); + => await Task.Run( + async () => + { + await Task.Delay(TimeSpan.FromSeconds(10), TestContext.CancellationToken); + waitException.Set(); + throw new InvalidOperationException(); + }, TestContext.CancellationToken).WithCancellationAsync(new CancellationTokenSource(TimeSpan.FromSeconds(1)).Token)); waitException.WaitOne(); - await Task.Delay(TimeSpan.FromSeconds(4)); - }, 3, TimeSpan.FromSeconds(3), _ => true); + await Task.Delay(TimeSpan.FromSeconds(4), TestContext.CancellationToken); + }, 3, TimeSpan.FromSeconds(3)); [TestMethod] public async Task CancellationAsyncWithReturnValue_ObserveException_Succeeds() @@ -95,7 +98,7 @@ public async Task CancellationAsyncWithReturnValue_ObserveException_Succeeds() await Assert.ThrowsAsync(async () => await Task.Run(async () => { - await Task.Delay(TimeSpan.FromSeconds(10)); + await Task.Delay(TimeSpan.FromSeconds(10), TestContext.CancellationToken); try { return 2; @@ -110,8 +113,8 @@ await Assert.ThrowsAsync(async () }).WithCancellationAsync(new CancellationTokenSource(TimeSpan.FromSeconds(1)).Token)); waitException.WaitOne(); - await Task.Delay(TimeSpan.FromSeconds(4)); - }, 3, TimeSpan.FromSeconds(3), _ => true); + await Task.Delay(TimeSpan.FromSeconds(4), TestContext.CancellationToken); + }, 3, TimeSpan.FromSeconds(3)); private static async Task DoSomething() { diff --git a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Helpers/TestExtension.cs b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Helpers/TestExtension.cs index 5d2729a8ab..404f11cd9a 100644 --- a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Helpers/TestExtension.cs +++ b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Helpers/TestExtension.cs @@ -5,13 +5,13 @@ namespace Microsoft.Testing.Platform.UnitTests.Helpers; internal class TestExtension : IExtension { - public string Uid { get; } = "Uid"; + public string Uid => "Uid"; - public string Version { get; } = "Version"; + public string Version => "Version"; - public string DisplayName { get; } = "DisplayName"; + public string DisplayName => "DisplayName"; - public string Description { get; } = "Description"; + public string Description => "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 78beb1d004..3a9fe86375 100644 --- a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/IPC/IPCTests.cs +++ b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/IPC/IPCTests.cs @@ -22,45 +22,46 @@ public IPCTests(TestContext testContext) [TestMethod] public async Task SingleConnectionNamedPipeServer_MultipleConnection_Fails() { - PipeNameDescription pipeNameDescription = NamedPipeServer.GetPipeName(Guid.NewGuid().ToString("N")); + PipeNameDescription pipeNameDescription = NamedPipeServer.GetPipeName(Guid.NewGuid().ToString("N"), new SystemEnvironment()); List openedPipe = []; List exceptions = []; ManualResetEventSlim waitException = new(false); - var waitTask = Task.Run(async () => - { - try + var waitTask = Task.Run( + async () => { - while (true) + try { - NamedPipeServer singleConnectionNamedPipeServer = new( - pipeNameDescription, - async _ => await Task.FromResult(VoidResponse.CachedInstance), - new SystemEnvironment(), - new Mock().Object, - new SystemTask(), - _testContext.CancellationTokenSource.Token); - - await singleConnectionNamedPipeServer.WaitConnectionAsync(_testContext.CancellationTokenSource.Token); - openedPipe.Add(singleConnectionNamedPipeServer); + while (true) + { + NamedPipeServer singleConnectionNamedPipeServer = new( + pipeNameDescription, + async _ => await Task.FromResult(VoidResponse.CachedInstance), + new SystemEnvironment(), + new Mock().Object, + new SystemTask(), + _testContext.CancellationToken); + + await singleConnectionNamedPipeServer.WaitConnectionAsync(_testContext.CancellationToken); + openedPipe.Add(singleConnectionNamedPipeServer); + } } - } - catch (Exception ex) - { - exceptions.Add(ex); - waitException.Set(); - } - }); + catch (Exception ex) + { + exceptions.Add(ex); + waitException.Set(); + } + }, _testContext.CancellationToken); NamedPipeClient namedPipeClient1 = new(pipeNameDescription.Name); - await namedPipeClient1.ConnectAsync(_testContext.CancellationTokenSource.Token); - waitException.Wait(); + await namedPipeClient1.ConnectAsync(_testContext.CancellationToken); + waitException.Wait(_testContext.CancellationToken); - Assert.AreEqual(1, openedPipe.Count); - Assert.AreEqual(1, exceptions.Count); + Assert.HasCount(1, openedPipe); + Assert.HasCount(1, exceptions); Assert.AreEqual(exceptions[0].GetType(), typeof(IOException)); - Assert.IsTrue(exceptions[0].Message.Contains("All pipe instances are busy.")); + Assert.Contains("All pipe instances are busy.", exceptions[0].Message); await waitTask; #if NETCOREAPP @@ -87,7 +88,7 @@ public async Task SingleConnectionNamedPipeServer_MultipleConnection_Fails() public async Task SingleConnectionNamedPipeServer_RequestReplySerialization_Succeeded() { Queue receivedMessages = new(); - PipeNameDescription pipeNameDescription = NamedPipeServer.GetPipeName(Guid.NewGuid().ToString("N")); + PipeNameDescription pipeNameDescription = NamedPipeServer.GetPipeName(Guid.NewGuid().ToString("N"), new SystemEnvironment()); NamedPipeClient namedPipeClient = new(pipeNameDescription.Name); namedPipeClient.RegisterSerializer(new VoidResponseSerializer(), typeof(VoidResponse)); namedPipeClient.RegisterSerializer(new TextMessageSerializer(), typeof(TextMessage)); @@ -95,29 +96,30 @@ public async Task SingleConnectionNamedPipeServer_RequestReplySerialization_Succ namedPipeClient.RegisterSerializer(new LongMessageSerializer(), typeof(LongMessage)); ManualResetEventSlim manualResetEventSlim = new(false); - var clientConnected = Task.Run(async () => - { - while (true) + var clientConnected = Task.Run( + async () => { - try - { - await namedPipeClient.ConnectAsync(CancellationToken.None); - manualResetEventSlim.Set(); - break; - } - catch (OperationCanceledException ct) when (ct.CancellationToken == _testContext.CancellationTokenSource.Token) - { - throw new OperationCanceledException("SingleConnectionNamedPipeServer_RequestReplySerialization_Succeeded cancellation during connect, testContext.CancellationTokenSource.Token"); - } - catch (OperationCanceledException) - { - throw new OperationCanceledException("SingleConnectionNamedPipeServer_RequestReplySerialization_Succeeded cancellation during connect"); - } - catch (Exception) + while (true) { + try + { + await namedPipeClient.ConnectAsync(CancellationToken.None); + manualResetEventSlim.Set(); + break; + } + catch (OperationCanceledException ct) when (ct.CancellationToken == _testContext.CancellationToken) + { + throw new OperationCanceledException("SingleConnectionNamedPipeServer_RequestReplySerialization_Succeeded cancellation during connect, testContext.CancellationTokenSource.Token"); + } + catch (OperationCanceledException) + { + throw new OperationCanceledException("SingleConnectionNamedPipeServer_RequestReplySerialization_Succeeded cancellation during connect"); + } + catch (Exception) + { + } } - } - }); + }, _testContext.CancellationToken); NamedPipeServer singleConnectionNamedPipeServer = new( pipeNameDescription, request => @@ -134,7 +136,7 @@ public async Task SingleConnectionNamedPipeServer_RequestReplySerialization_Succ singleConnectionNamedPipeServer.RegisterSerializer(new IntMessageSerializer(), typeof(IntMessage)); singleConnectionNamedPipeServer.RegisterSerializer(new LongMessageSerializer(), typeof(LongMessage)); await singleConnectionNamedPipeServer.WaitConnectionAsync(CancellationToken.None); - manualResetEventSlim.Wait(); + manualResetEventSlim.Wait(_testContext.CancellationToken); await clientConnected.WithCancellationAsync(CancellationToken.None); @@ -151,7 +153,7 @@ public async Task SingleConnectionNamedPipeServer_RequestReplySerialization_Succ { string currentString = RandomString(random.Next(1024, 1024 * 1024 * 2), random); await namedPipeClient.RequestReplyAsync(new TextMessage(currentString), CancellationToken.None); - Assert.AreEqual(1, receivedMessages.Count); + Assert.HasCount(1, receivedMessages); Assert.AreEqual(receivedMessages.Dequeue(), new TextMessage(currentString)); currentRound--; } @@ -170,9 +172,9 @@ public async Task SingleConnectionNamedPipeServer_RequestReplySerialization_Succ [TestMethod] public async Task ConnectionNamedPipeServer_MultipleConnection_Succeeds() { - PipeNameDescription pipeNameDescription = NamedPipeServer.GetPipeName(Guid.NewGuid().ToString("N")); + PipeNameDescription pipeNameDescription = NamedPipeServer.GetPipeName(Guid.NewGuid().ToString("N"), new SystemEnvironment()); - List pipes = new(); + List pipes = []; for (int i = 0; i < 3; i++) { pipes.Add(new( @@ -182,10 +184,10 @@ public async Task ConnectionNamedPipeServer_MultipleConnection_Succeeds() new Mock().Object, new SystemTask(), maxNumberOfServerInstances: 3, - _testContext.CancellationTokenSource.Token)); + _testContext.CancellationToken)); } - IOException exception = Assert.ThrowsException(() => + IOException exception = Assert.ThrowsExactly(() => new NamedPipeServer( pipeNameDescription, async _ => await Task.FromResult(VoidResponse.CachedInstance), @@ -193,29 +195,30 @@ public async Task ConnectionNamedPipeServer_MultipleConnection_Succeeds() new Mock().Object, new SystemTask(), maxNumberOfServerInstances: 3, - _testContext.CancellationTokenSource.Token)); - StringAssert.Contains(exception.Message, "All pipe instances are busy."); + _testContext.CancellationToken)); + Assert.Contains("All pipe instances are busy.", exception.Message); - List waitConnectionTask = new(); + List waitConnectionTask = []; int connectionCompleted = 0; foreach (NamedPipeServer namedPipeServer in pipes) { - waitConnectionTask.Add(Task.Run(async () => - { - await namedPipeServer.WaitConnectionAsync(_testContext.CancellationTokenSource.Token); - Interlocked.Increment(ref connectionCompleted); - })); + waitConnectionTask.Add(Task.Run( + async () => + { + await namedPipeServer.WaitConnectionAsync(_testContext.CancellationToken); + Interlocked.Increment(ref connectionCompleted); + }, _testContext.CancellationToken)); } - List connectedClients = new(); + List connectedClients = []; for (int i = 0; i < waitConnectionTask.Count; i++) { NamedPipeClient namedPipeClient = new(pipeNameDescription.Name); connectedClients.Add(namedPipeClient); - await namedPipeClient.ConnectAsync(_testContext.CancellationTokenSource.Token); + await namedPipeClient.ConnectAsync(_testContext.CancellationToken); } - await Task.WhenAll(waitConnectionTask.ToArray()); + await Task.WhenAll([.. waitConnectionTask]); Assert.AreEqual(3, connectionCompleted); @@ -235,8 +238,7 @@ public async Task ConnectionNamedPipeServer_MultipleConnection_Succeeds() private static string RandomString(int length, Random random) { const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; - return new string(Enumerable.Repeat(chars, length) - .Select(s => s[random.Next(s.Length)]).ToArray()); + return new string([.. Enumerable.Repeat(chars, length).Select(s => s[random.Next(s.Length)])]); } private abstract record BaseMessage : IRequest; diff --git a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/IPC/ProtocolTests.cs b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/IPC/ProtocolTests.cs index 019aa5219a..809a396a18 100644 --- a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/IPC/ProtocolTests.cs +++ b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/IPC/ProtocolTests.cs @@ -14,7 +14,7 @@ public void TestResultMessagesSerializeDeserialize() { var success = new SuccessfulTestResultMessage("uid", "displayName", 1, 100, "reason", "standardOutput", "errorOutput", "sessionUid"); var fail = new FailedTestResultMessage("uid", "displayName", 2, 200, "reason", [new ExceptionMessage("errorMessage", "errorType", "stackTrace")], "standardOutput", "errorOutput", "sessionUid"); - var message = new TestResultMessages("executionId", "instanceId", new[] { success }, new[] { fail }); + var message = new TestResultMessages("executionId", "instanceId", [success], [fail]); var stream = new MemoryStream(); new TestResultMessagesSerializer().Serialize(message, stream); @@ -26,4 +26,123 @@ public void TestResultMessagesSerializeDeserialize() #endif Assert.AreEqual(message.FailedTestMessages?[0].Exceptions?[0].ErrorMessage, actual.FailedTestMessages?[0].Exceptions?[0].ErrorMessage); } + + [TestMethod] + public void DiscoveredTestMessagesSerializeDeserialize() + { + var serializer = new DiscoveredTestMessagesSerializer(); + var stream = new MemoryStream(); + + var message = new DiscoveredTestMessages( + "MyExecId", + "MyInstId", + [ + new DiscoveredTestMessage("MyFirstUid", "DispName1", "path/to/file1.cs", 19, "MyNamespace1", "FirstType", "TM1", [], []), + new DiscoveredTestMessage("My2ndUid", "SecondDisplay", "file2.cs", 21, string.Empty, null, string.Empty, [], []), + new DiscoveredTestMessage("My3rdUid", "3rdDisplay", null, null, "MyNamespace3", "TestClass3", "TM3", [], [new("Key1", "Value"), new("Key2", string.Empty)]), + new DiscoveredTestMessage("My4thUid", "DispName1", "path/to/file1.cs", 19, "MyNamespace1", "FirstType", "TM1", ["paramtype1", "paramtype2"], []), + new DiscoveredTestMessage("My5thUid", "SecondDisplay", "file2.cs", 21, string.Empty, null, string.Empty, ["paramtype1", "paramtype2", "paramtype3"], []), + new DiscoveredTestMessage("My5thUid", "3rdDisplay", null, null, "MyNamespace3", "TestClass3", "TM3", ["paramtype1", "paramtype2", "paramtype3"], [new("Key1", "Value"), new("Key2", string.Empty)]), + ]); + + serializer.Serialize(message, stream); + stream.Seek(0, SeekOrigin.Begin); + + var deserialized = (DiscoveredTestMessages)serializer.Deserialize(stream); + Assert.IsNotNull(deserialized); + Assert.AreEqual(message.ExecutionId, deserialized.ExecutionId); + Assert.AreEqual(message.InstanceId, deserialized.InstanceId); + Assert.HasCount(message.DiscoveredMessages.Length, deserialized.DiscoveredMessages); + for (int i = 0; i < message.DiscoveredMessages.Length; i++) + { + DiscoveredTestMessage expected = message.DiscoveredMessages[i]; + DiscoveredTestMessage actual = deserialized.DiscoveredMessages[i]; + Assert.AreEqual(expected.Uid, actual.Uid); + Assert.AreEqual(expected.DisplayName, actual.DisplayName); + Assert.AreEqual(expected.FilePath, actual.FilePath); + Assert.AreEqual(expected.LineNumber, actual.LineNumber); + Assert.AreEqual(expected.Namespace, actual.Namespace); + Assert.AreEqual(expected.TypeName, actual.TypeName); + Assert.AreEqual(expected.MethodName, actual.MethodName); + + Assert.IsNotNull(expected.ParameterTypeFullNames); + Assert.IsNotNull(actual.ParameterTypeFullNames); + Assert.HasCount(expected.ParameterTypeFullNames.Length, actual.ParameterTypeFullNames); + for (int j = 0; j < expected.ParameterTypeFullNames.Length; j++) + { + Assert.AreEqual(expected.ParameterTypeFullNames[j], actual.ParameterTypeFullNames[j]); + } + + Assert.HasCount(expected.Traits.Length, actual.Traits); + for (int j = 0; j < expected.Traits.Length; j++) + { + Assert.AreEqual(expected.Traits[j].Key, actual.Traits[j].Key); + Assert.AreEqual(expected.Traits[j].Value, actual.Traits[j].Value); + } + } + } + + [TestMethod] + public void HandshakeMessageWithProperties() + { + var message = new HandshakeMessage(new Dictionary + { + { 10, "Ten" }, + { 35, "ThirtyFive" }, + { 48, "FortyEight" }, + }); + + var stream = new MemoryStream(); + new HandshakeMessageSerializer().Serialize(message, stream); + stream.Seek(0, SeekOrigin.Begin); + var actual = (HandshakeMessage)new HandshakeMessageSerializer().Deserialize(stream); + + Assert.IsNotNull(actual.Properties); + Assert.IsNotNull(message.Properties); + + Assert.HasCount(3, actual.Properties); + Assert.HasCount(3, message.Properties); + + Assert.AreEqual("Ten", actual.Properties[10]); + Assert.AreEqual("Ten", message.Properties[10]); + + Assert.AreEqual("ThirtyFive", actual.Properties[35]); + Assert.AreEqual("ThirtyFive", message.Properties[35]); + + Assert.AreEqual("FortyEight", actual.Properties[48]); + Assert.AreEqual("FortyEight", message.Properties[48]); + } + + [TestMethod] + public void HandshakeMessageWithEmptyProperties() + { + var message = new HandshakeMessage([]); + + var stream = new MemoryStream(); + new HandshakeMessageSerializer().Serialize(message, stream); + stream.Seek(0, SeekOrigin.Begin); + var actual = (HandshakeMessage)new HandshakeMessageSerializer().Deserialize(stream); + + Assert.IsNotNull(actual.Properties); + Assert.IsNotNull(message.Properties); + + Assert.IsEmpty(actual.Properties); + Assert.IsEmpty(message.Properties); + } + + [TestMethod] + public void HandshakeMessageWithNullProperties() + { + var message = new HandshakeMessage(null); + + var stream = new MemoryStream(); + new HandshakeMessageSerializer().Serialize(message, stream); + stream.Seek(0, SeekOrigin.Begin); + var actual = (HandshakeMessage)new HandshakeMessageSerializer().Deserialize(stream); + + Assert.IsNotNull(actual.Properties); + Assert.IsNull(message.Properties); + + Assert.IsEmpty(actual.Properties); + } } diff --git a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Logging/FileLoggerTests.cs b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Logging/FileLoggerTests.cs index 2541881cac..90e869eb9a 100644 --- a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Logging/FileLoggerTests.cs +++ b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Logging/FileLoggerTests.cs @@ -11,6 +11,9 @@ namespace Microsoft.Testing.Platform.UnitTests; [TestClass] public sealed class FileLoggerTests : IDisposable { + // https://github.com/microsoft/testfx/issues/6136 + public TestContext TestContext { get; set; } = null!; + private const string LogFolder = "aaa"; private const string LogPrefix = "bbb"; private const string FileName = "ccc"; @@ -110,7 +113,7 @@ public void FileLogger_NullFileSyncFlush_FileStreamCreationThrows() .Throws() .Returns(_mockStream.Object); - Assert.ThrowsException(() => _ = new FileLogger( + Assert.ThrowsExactly(() => _ = new FileLogger( new(LogFolder, LogPrefix, fileName: null, syncFlush: true), LogLevel.Trace, _mockClock.Object, @@ -128,7 +131,7 @@ public void FileLogger_NullFileSyncFlush_FileStreamCreationThrows() public void FileLogger_ValidFileName_FileStreamCreatedSuccessfully(bool syncFlush, bool fileExists) { string expectedPath = Path.Combine(LogFolder, FileName); - _mockFileSystem.Setup(x => x.Exists(expectedPath)).Returns(fileExists); + _mockFileSystem.Setup(x => x.ExistFile(expectedPath)).Returns(fileExists); _mockFileStreamFactory .Setup(x => x.Create(It.IsAny(), fileExists ? FileMode.Append : FileMode.CreateNew, FileAccess.Write, FileShare.Read)) .Returns(_mockStream.Object); @@ -156,7 +159,7 @@ public void FileLogger_ValidFileName_FileStreamCreatedSuccessfully(bool syncFlus [DynamicData(nameof(LogTestHelpers.GetLogLevelCombinations), typeof(LogTestHelpers))] public async Task Log_WhenSyncFlush_StreamWriterIsCalledOnlyWhenLogLevelAllowsIt(LogLevel defaultLogLevel, LogLevel currentLogLevel) { - _mockFileSystem.Setup(x => x.Exists(It.IsAny())).Returns(false); + _mockFileSystem.Setup(x => x.ExistFile(It.IsAny())).Returns(false); _mockFileStreamFactory .Setup(x => x.Create(It.IsAny(), FileMode.CreateNew, FileAccess.Write, FileShare.Read)) .Returns(_mockStream.Object); @@ -173,15 +176,15 @@ public async Task Log_WhenSyncFlush_StreamWriterIsCalledOnlyWhenLogLevelAllowsIt if (LogTestHelpers.IsLogEnabled(defaultLogLevel, currentLogLevel)) { - await _memoryStream.FlushAsync(); + await _memoryStream.FlushAsync(TestContext.CancellationToken); int iteration = 0; while (_memoryStream.Length == 0 && iteration < 10) { iteration++; - await Task.Delay(200); + await Task.Delay(200, TestContext.CancellationToken); } - await _memoryStream.FlushAsync(); + await _memoryStream.FlushAsync(TestContext.CancellationToken); _mockConsole.Verify(x => x.WriteLine(It.IsAny()), Times.Never); Assert.AreEqual($"[00:00:00.000 Test - {currentLogLevel}] Message{Environment.NewLine}", Encoding.Default.GetString(_memoryStream.ToArray())); @@ -196,7 +199,7 @@ public async Task Log_WhenSyncFlush_StreamWriterIsCalledOnlyWhenLogLevelAllowsIt [DynamicData(nameof(LogTestHelpers.GetLogLevelCombinations), typeof(LogTestHelpers))] public void Log_WhenAsyncFlush_StreamWriterIsCalledOnlyWhenLogLevelAllowsIt(LogLevel defaultLogLevel, LogLevel currentLogLevel) { - _mockFileSystem.Setup(x => x.Exists(It.IsAny())).Returns(false); + _mockFileSystem.Setup(x => x.ExistFile(It.IsAny())).Returns(false); _mockFileStreamFactory .Setup(x => x.Create(It.IsAny(), FileMode.CreateNew, FileAccess.Write, FileShare.Read)) .Returns(_mockStream.Object); diff --git a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Logging/LogTestHelpers.cs b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Logging/LogTestHelpers.cs index 571f312dff..cc3883e67b 100644 --- a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Logging/LogTestHelpers.cs +++ b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Logging/LogTestHelpers.cs @@ -21,8 +21,8 @@ public static IEnumerable GetLogLevelsForDynamicData() public static IEnumerable<(LogLevel DefaultLevel, LogLevel CurrentLevel)> GetLogLevelCombinations() { - List<(LogLevel, LogLevel)> logLevelCombinations = new(); - LogLevel[] logLevels = GetLogLevels().ToArray(); + List<(LogLevel, LogLevel)> logLevelCombinations = []; + LogLevel[] logLevels = [.. GetLogLevels()]; for (int i = 0; i < logLevels.Length; i++) { diff --git a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Messages/AsynchronousMessageBusTests.cs b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Messages/AsynchronousMessageBusTests.cs index 2d6ba5076e..dce3b35b02 100644 --- a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Messages/AsynchronousMessageBusTests.cs +++ b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Messages/AsynchronousMessageBusTests.cs @@ -13,6 +13,8 @@ namespace Microsoft.Testing.Platform.UnitTests; [TestClass] public sealed class AsynchronousMessageBusTests { + public TestContext TestContext { get; set; } + [TestMethod] public async Task UnexpectedTypePublished_ShouldFail() { @@ -56,7 +58,7 @@ public async Task DrainDataAsync_Loop_ShouldFail() } catch (InvalidOperationException ex) { - StringAssert.Contains(ex.Message, "Publisher/Consumer loop detected during the drain after"); + Assert.Contains("Publisher/Consumer loop detected during the drain after", ex.Message); } // Prevent loop to continue @@ -88,10 +90,10 @@ public async Task MessageBus_WhenConsumerProducesAndConsumesTheSameType_ShouldNo await proxy.DrainDataAsync(); // assert - Assert.AreEqual(1, consumerA.ConsumedData.Count); + Assert.HasCount(1, consumerA.ConsumedData); Assert.AreEqual(consumerBData, consumerA.ConsumedData[0]); - Assert.AreEqual(1, consumerB.ConsumedData.Count); + Assert.HasCount(1, consumerB.ConsumedData); Assert.AreEqual(consumerAData, consumerB.ConsumedData[0]); } @@ -105,7 +107,7 @@ public async Task Consumers_ConsumeData_ShouldNotMissAnyPayload() Random random = new(); for (int i = 0; i < totalConsumers; i++) { - DummyConsumer dummyConsumer = new(async _ => await Task.Delay(random.Next(40, 80))); + DummyConsumer dummyConsumer = new(async _ => await Task.Delay(random.Next(40, 80), TestContext.CancellationToken)); dummyConsumers.Add(dummyConsumer); } @@ -120,15 +122,14 @@ public async Task Consumers_ConsumeData_ShouldNotMissAnyPayload() proxy.SetBuiltMessageBus(asynchronousMessageBus); DummyConsumer.DummyProducer producer = new(); - await Task.WhenAll(Enumerable.Range(1, totalPayloads) - .Select(i => Task.Run(async () => await proxy.PublishAsync(producer, new DummyConsumer.DummyData { Data = i }))).ToArray()); + await Task.WhenAll([.. Enumerable.Range(1, totalPayloads).Select(i => Task.Run(async () => await proxy.PublishAsync(producer, new DummyConsumer.DummyData { Data = i }), TestContext.CancellationToken))]); await proxy.DrainDataAsync(); - Assert.AreEqual(totalConsumers, dummyConsumers.Count); + Assert.HasCount(totalConsumers, dummyConsumers); foreach (DummyConsumer consumer in dummyConsumers) { - Assert.AreEqual(totalPayloads, consumer.DummyDataList.Count); + Assert.HasCount(totalPayloads, consumer.DummyDataList); int i = 1; foreach (DummyConsumer.DummyData payload in consumer.DummyDataList.OrderBy(x => x.Data)) diff --git a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Messages/PropertyBagTests.cs b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Messages/PropertyBagTests.cs index 12f4ad6b23..e45f9d5d25 100644 --- a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Messages/PropertyBagTests.cs +++ b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Messages/PropertyBagTests.cs @@ -23,8 +23,8 @@ public void Ctors_CorrectlyInit() [TestMethod] public void Ctors_With_WrongInit_ShouldFail() { - Assert.ThrowsException(() => _ = new PropertyBag([new DummyProperty(), PassedTestNodeStateProperty.CachedInstance, PassedTestNodeStateProperty.CachedInstance])); - Assert.ThrowsException(() => _ = new PropertyBag(new IProperty[] { new DummyProperty(), PassedTestNodeStateProperty.CachedInstance, PassedTestNodeStateProperty.CachedInstance }.AsEnumerable())); + Assert.ThrowsExactly(() => _ = new PropertyBag([new DummyProperty(), PassedTestNodeStateProperty.CachedInstance, PassedTestNodeStateProperty.CachedInstance])); + Assert.ThrowsExactly(() => _ = new PropertyBag(new IProperty[] { new DummyProperty(), PassedTestNodeStateProperty.CachedInstance, PassedTestNodeStateProperty.CachedInstance }.AsEnumerable())); } [TestMethod] @@ -40,14 +40,14 @@ public void AddGet_Of_TestNodeStateProperty_Succed() PropertyBag property = new(); Assert.IsFalse(property.Any()); - Assert.AreEqual(0, property.OfType().Length); + Assert.IsEmpty(property.OfType()); property.Add(PassedTestNodeStateProperty.CachedInstance); Assert.AreEqual(PassedTestNodeStateProperty.CachedInstance, property.Single()); Assert.AreEqual(PassedTestNodeStateProperty.CachedInstance, property.SingleOrDefault()); Assert.IsTrue(property.Any()); - Assert.AreEqual(1, property.OfType().Length); + Assert.HasCount(1, property.OfType()); } [TestMethod] @@ -55,7 +55,7 @@ public void Add_Of_TestNodeStateProperty_More_Than_One_Time_Fail() { PropertyBag property = new(); property.Add(PassedTestNodeStateProperty.CachedInstance); - Assert.ThrowsException(() => property.Add(PassedTestNodeStateProperty.CachedInstance)); + Assert.ThrowsExactly(() => property.Add(PassedTestNodeStateProperty.CachedInstance)); } [TestMethod] @@ -64,7 +64,7 @@ public void Add_Same_Instance_More_Times_Fail() PropertyBag property = new(); DummyProperty dummyProperty = new(); property.Add(dummyProperty); - Assert.ThrowsException(() => property.Add(dummyProperty)); + Assert.ThrowsExactly(() => property.Add(dummyProperty)); } [TestMethod] @@ -92,7 +92,7 @@ public void SingleOrDefault_Should_Return_CorrectObject() Assert.IsNull(property.SingleOrDefault()); property.Add(new DummyProperty()); - Assert.ThrowsException(property.SingleOrDefault); + Assert.ThrowsExactly(property.SingleOrDefault); } [TestMethod] @@ -105,10 +105,10 @@ public void Single_Should_Return_CorrectObject() Assert.AreEqual(PassedTestNodeStateProperty.CachedInstance, property.Single()); Assert.AreEqual(prop, property.Single()); - Assert.ThrowsException(property.Single); + Assert.ThrowsExactly(property.Single); property.Add(new DummyProperty()); - Assert.ThrowsException(property.Single); + Assert.ThrowsExactly(property.Single); } [TestMethod] @@ -120,7 +120,7 @@ public void OfType_Should_Return_CorrectObject() property.Add(PassedTestNodeStateProperty.CachedInstance); Assert.AreEqual(PassedTestNodeStateProperty.CachedInstance, property.OfType().Single()); - Assert.AreEqual(2, property.OfType().Length); + Assert.HasCount(2, property.OfType()); } [TestMethod] @@ -141,15 +141,15 @@ public void AsEnumerable_Should_Return_CorrectItems() list.Remove(prop); } - Assert.AreEqual(0, list.Count); + Assert.IsEmpty(list); - list = property.AsEnumerable().ToList(); + list = [.. property.AsEnumerable()]; foreach (IProperty prop in property) { list.Remove(prop); } - Assert.AreEqual(0, list.Count); + Assert.IsEmpty(list); } [TestMethod] @@ -159,13 +159,13 @@ public void EmptyProperties_Should_NotFail() Assert.AreEqual(0, property.Count); Assert.IsFalse(property.Any()); Assert.IsNull(property.SingleOrDefault()); - Assert.ThrowsException(property.Single); - Assert.AreEqual(0, property.OfType().Length); + Assert.ThrowsExactly(property.Single); + Assert.IsEmpty(property.OfType()); Assert.AreEqual(0, property.AsEnumerable().Count()); foreach (IProperty item in property) { - Assert.IsTrue(false, "no item expected"); + Assert.Fail("no item expected"); } } diff --git a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Messages/TestNodePropertiesTests.cs b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Messages/TestNodePropertiesTests.cs index 849b57375f..2a91a44c32 100644 --- a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Messages/TestNodePropertiesTests.cs +++ b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Messages/TestNodePropertiesTests.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. +#pragma warning disable CS0618 // Type or member is obsolete namespace Microsoft.Testing.Platform.Extensions.Messages.UnitTests; [TestClass] diff --git a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Messages/TestNodeUidTests.cs b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Messages/TestNodeUidTests.cs index 6a57d3b664..794de47f8c 100644 --- a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Messages/TestNodeUidTests.cs +++ b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Messages/TestNodeUidTests.cs @@ -27,13 +27,13 @@ public void TestNodeUid_NullValue_ShouldFail() // Assert.Throw(() => { TestNodeUid testNode = null!; }); // Implicit conversion from a null, empty or whitespace string should throw. - Assert.ThrowsException(() => { TestNodeUid testNode = (string)null!; }); - Assert.ThrowsException(() => { TestNodeUid testNode = string.Empty; }); - Assert.ThrowsException(() => { TestNodeUid testNode = " "; }); + Assert.ThrowsExactly(() => { TestNodeUid testNode = (string)null!; }); + Assert.ThrowsExactly(() => { TestNodeUid testNode = string.Empty; }); + Assert.ThrowsExactly(() => { TestNodeUid testNode = " "; }); // Providing null, empty, or whitespace id should throw. - Assert.ThrowsException(() => { TestNodeUid testNode = new(null!); }); - Assert.ThrowsException(() => { TestNodeUid testNode = new(string.Empty); }); - Assert.ThrowsException(() => { TestNodeUid testNode = new(" "); }); + Assert.ThrowsExactly(() => { TestNodeUid testNode = new(null!); }); + Assert.ThrowsExactly(() => { TestNodeUid testNode = new(string.Empty); }); + Assert.ThrowsExactly(() => { TestNodeUid testNode = new(" "); }); } } diff --git a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/OutputDevice/Terminal/TerminalTestReporterTests.cs b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/OutputDevice/Terminal/TerminalTestReporterTests.cs index 0e3467d86c..2ed465160d 100644 --- a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/OutputDevice/Terminal/TerminalTestReporterTests.cs +++ b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/OutputDevice/Terminal/TerminalTestReporterTests.cs @@ -3,6 +3,7 @@ using Microsoft.Testing.Platform.Helpers; using Microsoft.Testing.Platform.OutputDevice.Terminal; +using Microsoft.Testing.Platform.Services; namespace Microsoft.Testing.Platform.UnitTests; @@ -27,12 +28,14 @@ public void AppendStackFrameFormatsStackTraceLineCorrectly() TerminalTestReporter.AppendStackFrame(terminal, firstStackTraceLine); #if NETCOREAPP - StringAssert.Contains(terminal.Output, " at Microsoft.Testing.Platform.UnitTests.TerminalTestReporterTests.AppendStackFrameFormatsStackTraceLineCorrectly() in "); + Assert.Contains(" at Microsoft.Testing.Platform.UnitTests.TerminalTestReporterTests.AppendStackFrameFormatsStackTraceLineCorrectly() in ", terminal.Output); #else - StringAssert.Contains(terminal.Output, " at Microsoft.Testing.Platform.UnitTests.TerminalTestReporterTests.AppendStackFrameFormatsStackTraceLineCorrectly()"); + // This is caused by us using portable symbols, and .NET Framework 4.6.2, once we update to .NET Framework 4.7.2 the path to file will be included in the stacktrace and this won't be necessary. + // See first point here: https://learn.microsoft.com/en-us/dotnet/core/diagnostics/symbols#support-for-portable-pdbs + Assert.Contains(" at Microsoft.Testing.Platform.UnitTests.TerminalTestReporterTests.AppendStackFrameFormatsStackTraceLineCorrectly()", terminal.Output); #endif // Line number without the respective file - Assert.IsFalse(terminal.Output.Contains(" :0")); + Assert.DoesNotContain(" :0", terminal.Output); } // Code with line when we have symbols @@ -64,17 +67,120 @@ public void StackTraceRegexCapturesLines(string stackTraceLine, string expected) } [TestMethod] - public void OutputFormattingIsCorrect() + public void NonAnsiTerminal_OutputFormattingIsCorrect() { + string targetFramework = "net8.0"; + string architecture = "x64"; + string assembly = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? @"C:\work\assembly.dll" : "/mnt/work/assembly.dll"; + var stringBuilderConsole = new StringBuilderConsole(); - var terminalReporter = new TerminalTestReporter(stringBuilderConsole, new TerminalTestReporterOptions + var terminalReporter = new TerminalTestReporter(assembly, targetFramework, architecture, stringBuilderConsole, new CTRLPlusCCancellationTokenSource(), new TerminalTestReporterOptions { ShowPassedTests = () => true, + + // Like --no-ansi in commandline, should disable ANSI altogether. + UseAnsi = false, + + ShowProgress = () => false, + }); + + DateTimeOffset startTime = DateTimeOffset.MinValue; + DateTimeOffset endTime = DateTimeOffset.MaxValue; + terminalReporter.TestExecutionStarted(startTime, 1, isDiscovery: false); + + string folder = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? @"C:\work\" : "/mnt/work/"; + string folderNoSlash = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? @"C:\work" : "/mnt/work"; + string folderLink = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? @"C:/work/" : "mnt/work/"; + string folderLinkNoSlash = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? @"C:/work" : "mnt/work"; + + terminalReporter.AssemblyRunStarted(); + string standardOutput = "Hello!"; + string errorOutput = "Oh no!"; + + terminalReporter.TestCompleted(testNodeUid: "PassedTest1", "PassedTest1", TestOutcome.Passed, TimeSpan.FromSeconds(10), + informativeMessage: null, errorMessage: null, exception: null, expected: null, actual: null, standardOutput, errorOutput); + terminalReporter.TestCompleted(testNodeUid: "SkippedTest1", "SkippedTest1", TestOutcome.Skipped, TimeSpan.FromSeconds(10), + informativeMessage: null, errorMessage: null, exception: null, expected: null, actual: null, standardOutput, errorOutput); + // timed out + canceled + failed should all report as failed in summary + terminalReporter.TestCompleted(testNodeUid: "TimedoutTest1", "TimedoutTest1", TestOutcome.Timeout, TimeSpan.FromSeconds(10), + informativeMessage: null, errorMessage: null, exception: null, expected: null, actual: null, standardOutput, errorOutput); + terminalReporter.TestCompleted(testNodeUid: "CanceledTest1", "CanceledTest1", TestOutcome.Canceled, TimeSpan.FromSeconds(10), + informativeMessage: null, errorMessage: null, exception: null, expected: null, actual: null, standardOutput, errorOutput); + terminalReporter.TestCompleted(testNodeUid: "FailedTest1", "FailedTest1", TestOutcome.Fail, TimeSpan.FromSeconds(10), + informativeMessage: null, errorMessage: "Tests failed", exception: new StackTraceException(@$" at FailingTest() in {folder}codefile.cs:line 10"), expected: "ABC", actual: "DEF", standardOutput, errorOutput); + terminalReporter.ArtifactAdded(outOfProcess: true, testName: null, @$"{folder}artifact1.txt"); + terminalReporter.ArtifactAdded(outOfProcess: false, testName: null, @$"{folder}artifact2.txt"); + terminalReporter.AssemblyRunCompleted(); + terminalReporter.TestExecutionCompleted(endTime); + + string output = stringBuilderConsole.Output; + + string expected = $""" + passed PassedTest1 (10s 000ms) + Standard output + Hello! + Error output + Oh no! + skipped SkippedTest1 (10s 000ms) + Standard output + Hello! + Error output + Oh no! + failed (canceled) TimedoutTest1 (10s 000ms) + Standard output + Hello! + Error output + Oh no! + failed (canceled) CanceledTest1 (10s 000ms) + Standard output + Hello! + Error output + Oh no! + failed FailedTest1 (10s 000ms) + Tests failed + Expected + ABC + Actual + DEF + at FailingTest() in {folder}codefile.cs:10 + Standard output + Hello! + Error output + Oh no! + + Out of process file artifacts produced: + - {folder}artifact1.txt + In process file artifacts produced: + - {folder}artifact2.txt + + Test run summary: Failed! - {assembly} (net8.0|x64) + total: 5 + failed: 3 + succeeded: 1 + skipped: 1 + duration: 3652058d 23h 59m 59s 999ms + + """; + + Assert.AreEqual(expected, ShowEscape(output)); + } + + [TestMethod] + public void SimpleAnsiTerminal_OutputFormattingIsCorrect() + { + string targetFramework = "net8.0"; + string architecture = "x64"; + string assembly = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? @"C:\work\assembly.dll" : "/mnt/work/assembly.dll"; + + var stringBuilderConsole = new StringBuilderConsole(); + var terminalReporter = new TerminalTestReporter(assembly, targetFramework, architecture, stringBuilderConsole, new CTRLPlusCCancellationTokenSource(), new TerminalTestReporterOptions + { + ShowPassedTests = () => true, + // Like if we autodetect that we are in CI (e.g. by looking at TF_BUILD, and we don't disable ANSI. UseAnsi = true, + UseCIAnsi = true, ForceAnsi = true, - ShowAssembly = false, - ShowAssemblyStartAndComplete = false, ShowProgress = () => false, }); @@ -82,60 +188,157 @@ public void OutputFormattingIsCorrect() DateTimeOffset endTime = DateTimeOffset.MaxValue; terminalReporter.TestExecutionStarted(startTime, 1, isDiscovery: false); + string folder = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? @"C:\work\" : "/mnt/work/"; + string folderNoSlash = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? @"C:\work" : "/mnt/work"; + string folderLink = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? @"C:/work/" : "mnt/work/"; + string folderLinkNoSlash = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? @"C:/work" : "mnt/work"; + + terminalReporter.AssemblyRunStarted(); + string standardOutput = "Hello!"; + string errorOutput = "Oh no!"; + + terminalReporter.TestCompleted(testNodeUid: "PassedTest1", "PassedTest1", TestOutcome.Passed, TimeSpan.FromSeconds(10), + informativeMessage: null, errorMessage: null, exception: null, expected: null, actual: null, standardOutput, errorOutput); + terminalReporter.TestCompleted(testNodeUid: "SkippedTest1", "SkippedTest1", TestOutcome.Skipped, TimeSpan.FromSeconds(10), + informativeMessage: null, errorMessage: null, exception: null, expected: null, actual: null, standardOutput, errorOutput); + // timed out + canceled + failed should all report as failed in summary + terminalReporter.TestCompleted(testNodeUid: "TimedoutTest1", "TimedoutTest1", TestOutcome.Timeout, TimeSpan.FromSeconds(10), + informativeMessage: null, errorMessage: null, exception: null, expected: null, actual: null, standardOutput, errorOutput); + terminalReporter.TestCompleted(testNodeUid: "CanceledTest1", "CanceledTest1", TestOutcome.Canceled, TimeSpan.FromSeconds(10), + informativeMessage: null, errorMessage: null, exception: null, expected: null, actual: null, standardOutput, errorOutput); + terminalReporter.TestCompleted(testNodeUid: "FailedTest1", "FailedTest1", TestOutcome.Fail, TimeSpan.FromSeconds(10), + informativeMessage: null, errorMessage: "Tests failed", exception: new StackTraceException(@$" at FailingTest() in {folder}codefile.cs:line 10"), expected: "ABC", actual: "DEF", standardOutput, errorOutput); + terminalReporter.ArtifactAdded(outOfProcess: true, testName: null, @$"{folder}artifact1.txt"); + terminalReporter.ArtifactAdded(outOfProcess: false, testName: null, @$"{folder}artifact2.txt"); + terminalReporter.AssemblyRunCompleted(); + terminalReporter.TestExecutionCompleted(endTime); + + string output = stringBuilderConsole.Output; + + string expected = $""" + ␛[32mpassed␛[m PassedTest1␛[90m ␛[90m(10s 000ms)␛[m + ␛[90m Standard output + ␛[90m Hello! + ␛[90m Error output + ␛[90m Oh no! + ␛[m␛[33mskipped␛[m SkippedTest1␛[90m ␛[90m(10s 000ms)␛[m + ␛[90m Standard output + ␛[90m Hello! + ␛[90m Error output + ␛[90m Oh no! + ␛[m␛[31mfailed (canceled)␛[m TimedoutTest1␛[90m ␛[90m(10s 000ms)␛[m + ␛[90m Standard output + ␛[90m Hello! + ␛[90m Error output + ␛[90m Oh no! + ␛[m␛[31mfailed (canceled)␛[m CanceledTest1␛[90m ␛[90m(10s 000ms)␛[m + ␛[90m Standard output + ␛[90m Hello! + ␛[90m Error output + ␛[90m Oh no! + ␛[m␛[31mfailed␛[m FailedTest1␛[90m ␛[90m(10s 000ms)␛[m + ␛[31m Tests failed + ␛[m␛[31m Expected + ␛[31m ABC + ␛[31m Actual + ␛[31m DEF + ␛[m␛[90m at FailingTest() in {folder}codefile.cs:10␛[90m + ␛[m␛[90m Standard output + ␛[90m Hello! + ␛[90m Error output + ␛[90m Oh no! + ␛[m + Out of process file artifacts produced: + - {folder}artifact1.txt + In process file artifacts produced: + - {folder}artifact2.txt + + ␛[31mTest run summary: Failed!␛[90m - ␛[m{folder}assembly.dll (net8.0|x64) + ␛[m total: 5 + ␛[31m failed: 3 + ␛[m succeeded: 1 + skipped: 1 + duration: 3652058d 23h 59m 59s 999ms + + """; + + Assert.AreEqual(expected, ShowEscape(output)); + } + + [TestMethod] + public void AnsiTerminal_OutputFormattingIsCorrect() + { string targetFramework = "net8.0"; string architecture = "x64"; string assembly = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? @"C:\work\assembly.dll" : "/mnt/work/assembly.dll"; + + var stringBuilderConsole = new StringBuilderConsole(); + var terminalReporter = new TerminalTestReporter(assembly, targetFramework, architecture, stringBuilderConsole, new CTRLPlusCCancellationTokenSource(), new TerminalTestReporterOptions + { + ShowPassedTests = () => true, + // Like if we autodetect that we are in ANSI capable terminal. + UseAnsi = true, + UseCIAnsi = false, + ForceAnsi = true, + + ShowProgress = () => false, + }); + + DateTimeOffset startTime = DateTimeOffset.MinValue; + DateTimeOffset endTime = DateTimeOffset.MaxValue; + terminalReporter.TestExecutionStarted(startTime, 1, isDiscovery: false); + string folder = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? @"C:\work\" : "/mnt/work/"; string folderNoSlash = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? @"C:\work" : "/mnt/work"; string folderLink = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? @"C:/work/" : "mnt/work/"; string folderLinkNoSlash = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? @"C:/work" : "mnt/work"; - terminalReporter.AssemblyRunStarted(assembly, targetFramework, architecture); + terminalReporter.AssemblyRunStarted(); string standardOutput = "Hello!"; string errorOutput = "Oh no!"; - terminalReporter.TestCompleted(assembly, targetFramework, architecture, testNodeUid: "PassedTest1", "PassedTest1", TestOutcome.Passed, TimeSpan.FromSeconds(10), + terminalReporter.TestCompleted(testNodeUid: "PassedTest1", "PassedTest1", TestOutcome.Passed, TimeSpan.FromSeconds(10), informativeMessage: null, errorMessage: null, exception: null, expected: null, actual: null, standardOutput, errorOutput); - terminalReporter.TestCompleted(assembly, targetFramework, architecture, testNodeUid: "SkippedTest1", "SkippedTest1", TestOutcome.Skipped, TimeSpan.FromSeconds(10), + terminalReporter.TestCompleted(testNodeUid: "SkippedTest1", "SkippedTest1", TestOutcome.Skipped, TimeSpan.FromSeconds(10), informativeMessage: null, errorMessage: null, exception: null, expected: null, actual: null, standardOutput, errorOutput); // timed out + canceled + failed should all report as failed in summary - terminalReporter.TestCompleted(assembly, targetFramework, architecture, testNodeUid: "TimedoutTest1", "TimedoutTest1", TestOutcome.Timeout, TimeSpan.FromSeconds(10), + terminalReporter.TestCompleted(testNodeUid: "TimedoutTest1", "TimedoutTest1", TestOutcome.Timeout, TimeSpan.FromSeconds(10), informativeMessage: null, errorMessage: null, exception: null, expected: null, actual: null, standardOutput, errorOutput); - terminalReporter.TestCompleted(assembly, targetFramework, architecture, testNodeUid: "CanceledTest1", "CanceledTest1", TestOutcome.Canceled, TimeSpan.FromSeconds(10), + terminalReporter.TestCompleted(testNodeUid: "CanceledTest1", "CanceledTest1", TestOutcome.Canceled, TimeSpan.FromSeconds(10), informativeMessage: null, errorMessage: null, exception: null, expected: null, actual: null, standardOutput, errorOutput); - terminalReporter.TestCompleted(assembly, targetFramework, architecture, testNodeUid: "FailedTest1", "FailedTest1", TestOutcome.Fail, TimeSpan.FromSeconds(10), + terminalReporter.TestCompleted(testNodeUid: "FailedTest1", "FailedTest1", TestOutcome.Fail, TimeSpan.FromSeconds(10), informativeMessage: null, errorMessage: "Tests failed", exception: new StackTraceException(@$" at FailingTest() in {folder}codefile.cs:line 10"), expected: "ABC", actual: "DEF", standardOutput, errorOutput); - terminalReporter.ArtifactAdded(outOfProcess: true, assembly, targetFramework, architecture, testName: null, @$"{folder}artifact1.txt"); - terminalReporter.ArtifactAdded(outOfProcess: false, assembly, targetFramework, architecture, testName: null, @$"{folder}artifact2.txt"); - terminalReporter.AssemblyRunCompleted(assembly, targetFramework, architecture, exitCode: null, outputData: null, errorData: null); + terminalReporter.ArtifactAdded(outOfProcess: true, testName: null, @$"{folder}artifact1.txt"); + terminalReporter.ArtifactAdded(outOfProcess: false, testName: null, @$"{folder}artifact2.txt"); + terminalReporter.AssemblyRunCompleted(); terminalReporter.TestExecutionCompleted(endTime); string output = stringBuilderConsole.Output; string expected = $""" - ␛[92mpassed␛[m PassedTest1␛[90m ␛[90m(10s 000ms)␛[m + ␛[32mpassed␛[m PassedTest1␛[90m ␛[90m(10s 000ms)␛[m ␛[90m Standard output Hello! Error output Oh no! - ␛[m␛[93mskipped␛[m SkippedTest1␛[90m ␛[90m(10s 000ms)␛[m + ␛[m␛[33mskipped␛[m SkippedTest1␛[90m ␛[90m(10s 000ms)␛[m ␛[90m Standard output Hello! Error output Oh no! - ␛[m␛[91mfailed (canceled)␛[m TimedoutTest1␛[90m ␛[90m(10s 000ms)␛[m + ␛[m␛[31mfailed (canceled)␛[m TimedoutTest1␛[90m ␛[90m(10s 000ms)␛[m ␛[90m Standard output Hello! Error output Oh no! - ␛[m␛[91mfailed (canceled)␛[m CanceledTest1␛[90m ␛[90m(10s 000ms)␛[m + ␛[m␛[31mfailed (canceled)␛[m CanceledTest1␛[90m ␛[90m(10s 000ms)␛[m ␛[90m Standard output Hello! Error output Oh no! - ␛[m␛[91mfailed␛[m FailedTest1␛[90m ␛[90m(10s 000ms)␛[m - ␛[91m Tests failed - ␛[m␛[91m Expected + ␛[m␛[31mfailed␛[m FailedTest1␛[90m ␛[90m(10s 000ms)␛[m + ␛[31m Tests failed + ␛[m␛[31m Expected ABC Actual DEF @@ -149,33 +352,37 @@ Oh no! - ␛[90m␛]8;;file:///{folderLink}artifact1.txt␛\{folder}artifact1.txt␛]8;;␛\␛[m In process file artifacts produced: - ␛[90m␛]8;;file:///{folderLink}artifact2.txt␛\{folder}artifact2.txt␛]8;;␛\␛[m - - ␛[91mTest run summary: Failed!␛[90m - ␛[m␛[90m␛]8;;file:///{folderLinkNoSlash}␛\{folder}assembly.dll␛]8;;␛\␛[m (net8.0|x64) + + ␛[31mTest run summary: Failed!␛[90m - ␛[m␛[90m␛]8;;file:///{folderLinkNoSlash}␛\{folder}assembly.dll␛]8;;␛\␛[m (net8.0|x64) ␛[m total: 5 - ␛[91m failed: 3 + ␛[31m failed: 3 ␛[m succeeded: 1 skipped: 1 duration: 3652058d 23h 59m 59s 999ms - + """; Assert.AreEqual(expected, ShowEscape(output)); } [TestMethod] - public void OutputProgressFrameIsCorrect() + public void AnsiTerminal_OutputProgressFrameIsCorrect() { + string targetFramework = "net8.0"; + string architecture = "x64"; + string assembly = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? @"C:\work\assembly.dll" : "/mnt/work/assembly.dll"; + var stringBuilderConsole = new StringBuilderConsole(); var stopwatchFactory = new StopwatchFactory(); - var terminalReporter = new TerminalTestReporter(stringBuilderConsole, new TerminalTestReporterOptions + var terminalReporter = new TerminalTestReporter(assembly, targetFramework, architecture, stringBuilderConsole, new CTRLPlusCCancellationTokenSource(), new TerminalTestReporterOptions { ShowPassedTests = () => true, + // Like if we autodetect that we are in ANSI capable terminal. UseAnsi = true, + UseCIAnsi = false, ForceAnsi = true, ShowActiveTests = true, - ShowAssembly = false, - ShowAssemblyStartAndComplete = false, ShowProgress = () => true, }) { @@ -193,34 +400,31 @@ public void OutputProgressFrameIsCorrect() DateTimeOffset endTime = DateTimeOffset.MaxValue; terminalReporter.TestExecutionStarted(startTime, 1, isDiscovery: false); - string targetFramework = "net8.0"; - string architecture = "x64"; - string assembly = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? @"C:\work\assembly.dll" : "/mnt/work/assembly.dll"; string folder = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? @"C:\work\" : "/mnt/work/"; string folderNoSlash = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? @"C:\work" : "/mnt/work"; string folderLink = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? @"C:/work/" : "mnt/work/"; string folderLinkNoSlash = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? @"C:/work" : "mnt/work"; - terminalReporter.AssemblyRunStarted(assembly, targetFramework, architecture); + terminalReporter.AssemblyRunStarted(); string standardOutput = "Hello!"; string errorOutput = "Oh no!"; // Note: Add 1ms to make the order of the progress frame deterministic. // Otherwise all tests that run for 1m31s could show in any order. - terminalReporter.TestInProgress(assembly, targetFramework, architecture, testNodeUid: "PassedTest1", displayName: "PassedTest1"); + terminalReporter.TestInProgress(testNodeUid: "PassedTest1", displayName: "PassedTest1"); stopwatchFactory.AddTime(TimeSpan.FromMilliseconds(1)); - terminalReporter.TestInProgress(assembly, targetFramework, architecture, testNodeUid: "SkippedTest1", displayName: "SkippedTest1"); + terminalReporter.TestInProgress(testNodeUid: "SkippedTest1", displayName: "SkippedTest1"); stopwatchFactory.AddTime(TimeSpan.FromMilliseconds(1)); - terminalReporter.TestInProgress(assembly, targetFramework, architecture, testNodeUid: "InProgressTest1", displayName: "InProgressTest1"); + terminalReporter.TestInProgress(testNodeUid: "InProgressTest1", displayName: "InProgressTest1"); stopwatchFactory.AddTime(TimeSpan.FromMinutes(1)); - terminalReporter.TestInProgress(assembly, targetFramework, architecture, testNodeUid: "InProgressTest2", displayName: "InProgressTest2"); + terminalReporter.TestInProgress(testNodeUid: "InProgressTest2", displayName: "InProgressTest2"); stopwatchFactory.AddTime(TimeSpan.FromSeconds(30)); - terminalReporter.TestInProgress(assembly, targetFramework, architecture, testNodeUid: "InProgressTest3", displayName: "InProgressTest3"); + terminalReporter.TestInProgress(testNodeUid: "InProgressTest3", displayName: "InProgressTest3"); stopwatchFactory.AddTime(TimeSpan.FromSeconds(1)); - terminalReporter.TestCompleted(assembly, targetFramework, architecture, testNodeUid: "PassedTest1", "PassedTest1", TestOutcome.Passed, TimeSpan.FromSeconds(10), + terminalReporter.TestCompleted(testNodeUid: "PassedTest1", "PassedTest1", TestOutcome.Passed, TimeSpan.FromSeconds(10), informativeMessage: null, errorMessage: null, exception: null, expected: null, actual: null, standardOutput, errorOutput); - terminalReporter.TestCompleted(assembly, targetFramework, architecture, testNodeUid: "SkippedTest1", "SkippedTest1", TestOutcome.Skipped, TimeSpan.FromSeconds(10), + terminalReporter.TestCompleted(testNodeUid: "SkippedTest1", "SkippedTest1", TestOutcome.Skipped, TimeSpan.FromSeconds(10), informativeMessage: null, errorMessage: null, exception: null, expected: null, actual: null, standardOutput, errorOutput); string output = stringBuilderConsole.Output; @@ -233,29 +437,29 @@ public void OutputProgressFrameIsCorrect() // Note: The progress is drawn after each completed event. string expected = $""" - {busyIndicatorString}␛[?25l␛[92mpassed␛[m PassedTest1␛[90m ␛[90m(10s 000ms)␛[m + {busyIndicatorString}␛[?25l␛[32mpassed␛[m PassedTest1␛[90m ␛[90m(10s 000ms)␛[m ␛[90m Standard output Hello! Error output Oh no! ␛[m - [␛[92m✓1␛[m/␛[91mx0␛[m/␛[93m↓0␛[m] assembly.dll (net8.0|x64)␛[2147483640G(1m 31s) + [␛[32m✓1␛[m/␛[31mx0␛[m/␛[33m↓0␛[m] assembly.dll (net8.0|x64)␛[2147483640G(1m 31s) SkippedTest1␛[2147483640G(1m 31s) InProgressTest1␛[2147483640G(1m 31s) InProgressTest2␛[2147483643G(31s) InProgressTest3␛[2147483644G(1s) ␛[7F - ␛[J␛[93mskipped␛[m SkippedTest1␛[90m ␛[90m(10s 000ms)␛[m + ␛[J␛[33mskipped␛[m SkippedTest1␛[90m ␛[90m(10s 000ms)␛[m ␛[90m Standard output Hello! Error output Oh no! ␛[m - [␛[92m✓1␛[m/␛[91mx0␛[m/␛[93m↓1␛[m] assembly.dll (net8.0|x64)␛[2147483640G(1m 31s) + [␛[32m✓1␛[m/␛[31mx0␛[m/␛[33m↓1␛[m] assembly.dll (net8.0|x64)␛[2147483640G(1m 31s) InProgressTest1␛[2147483640G(1m 31s) InProgressTest2␛[2147483643G(31s) InProgressTest3␛[2147483644G(1s) - + """; Assert.AreEqual(expected, ShowEscape(output)); @@ -392,4 +596,221 @@ private class StackTraceException : Exception public override string? StackTrace { get; } } + + // Test data for all C0 control characters (U+0000-U+001F) that are normalized + [DataRow('\x0000', '\x2400', "NULL")] + [DataRow('\x0001', '\x2401', "START OF HEADING")] + [DataRow('\x0002', '\x2402', "START OF TEXT")] + [DataRow('\x0003', '\x2403', "END OF TEXT")] + [DataRow('\x0004', '\x2404', "END OF TRANSMISSION")] + [DataRow('\x0005', '\x2405', "ENQUIRY")] + [DataRow('\x0006', '\x2406', "ACKNOWLEDGE")] + [DataRow('\x0007', '\x2407', "BELL")] + [DataRow('\x0008', '\x2408', "BACKSPACE")] + [DataRow('\t', '\x2409', "TAB")] + [DataRow('\n', '\x240A', "LINE FEED")] + [DataRow('\x000B', '\x240B', "VERTICAL TAB")] + [DataRow('\x000C', '\x240C', "FORM FEED")] + [DataRow('\r', '\x240D', "CARRIAGE RETURN")] + [DataRow('\x000E', '\x240E', "SHIFT OUT")] + [DataRow('\x000F', '\x240F', "SHIFT IN")] + [DataRow('\x0010', '\x2410', "DATA LINK ESCAPE")] + [DataRow('\x0011', '\x2411', "DEVICE CONTROL ONE")] + [DataRow('\x0012', '\x2412', "DEVICE CONTROL TWO")] + [DataRow('\x0013', '\x2413', "DEVICE CONTROL THREE")] + [DataRow('\x0014', '\x2414', "DEVICE CONTROL FOUR")] + [DataRow('\x0015', '\x2415', "NEGATIVE ACKNOWLEDGE")] + [DataRow('\x0016', '\x2416', "SYNCHRONOUS IDLE")] + [DataRow('\x0017', '\x2417', "END OF TRANSMISSION BLOCK")] + [DataRow('\x0018', '\x2418', "CANCEL")] + [DataRow('\x0019', '\x2419', "END OF MEDIUM")] + [DataRow('\x001A', '\x241A', "SUBSTITUTE")] + [DataRow('\x001B', '\x241B', "ESCAPE")] + [DataRow('\x001C', '\x241C', "FILE SEPARATOR")] + [DataRow('\x001D', '\x241D', "GROUP SEPARATOR")] + [DataRow('\x001E', '\x241E', "RECORD SEPARATOR")] + [DataRow('\x001F', '\x241F', "UNIT SEPARATOR")] + [TestMethod] + public void TestDisplayNames_WithControlCharacters_AreNormalized(char controlChar, char expectedChar, string charName) + { + string targetFramework = "net8.0"; + string architecture = "x64"; + string assembly = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? @"C:\work\assembly.dll" : "/mnt/work/assembly.dll"; + + var stringBuilderConsole = new StringBuilderConsole(); + var terminalReporter = new TerminalTestReporter(assembly, targetFramework, architecture, stringBuilderConsole, new CTRLPlusCCancellationTokenSource(), new TerminalTestReporterOptions + { + ShowPassedTests = () => true, + UseAnsi = false, + ShowProgress = () => false, + }); + + DateTimeOffset startTime = DateTimeOffset.MinValue; + DateTimeOffset endTime = DateTimeOffset.MaxValue; + terminalReporter.TestExecutionStarted(startTime, 1, isDiscovery: false); + + terminalReporter.AssemblyRunStarted(); + + // Test display name with the specific control character + string testDisplayName = $"Test{controlChar}Name"; + terminalReporter.TestCompleted(testNodeUid: "Test1", testDisplayName, TestOutcome.Passed, TimeSpan.FromSeconds(1), + informativeMessage: null, errorMessage: null, exception: null, expected: null, actual: null, standardOutput: null, errorOutput: null); + + terminalReporter.AssemblyRunCompleted(); + terminalReporter.TestExecutionCompleted(endTime); + + string output = stringBuilderConsole.Output; + + // Verify that the control character is replaced with its Unicode control picture + string normalizedDisplayName = $"Test{expectedChar}Name"; + Assert.Contains(normalizedDisplayName, output, $"{charName} should be replaced with {expectedChar}"); + + // Verify that the literal control character is not present in the test display name + // Note: We skip this assertion for whitespace characters (\t, \n, \r) because these + // characters naturally appear in console output formatting (e.g., line breaks between tests) + // and asserting their complete absence would cause false positives + string literalDisplayName = $"Test{controlChar}Name"; + bool isWhitespaceChar = controlChar is '\t' or '\n' or '\r'; + if (!isWhitespaceChar) + { + Assert.DoesNotContain(literalDisplayName, output, $"Literal {charName} should not be present in test display name"); + } + } + + // Test data for all C0 control characters (U+0000-U+001F) that are normalized + [DataRow('\x0000', '\x2400', "NULL")] + [DataRow('\x0001', '\x2401', "START OF HEADING")] + [DataRow('\x0002', '\x2402', "START OF TEXT")] + [DataRow('\x0003', '\x2403', "END OF TEXT")] + [DataRow('\x0004', '\x2404', "END OF TRANSMISSION")] + [DataRow('\x0005', '\x2405', "ENQUIRY")] + [DataRow('\x0006', '\x2406', "ACKNOWLEDGE")] + [DataRow('\x0007', '\x2407', "BELL")] + [DataRow('\x0008', '\x2408', "BACKSPACE")] + [DataRow('\t', '\x2409', "TAB")] + [DataRow('\n', '\x240A', "LINE FEED")] + [DataRow('\x000B', '\x240B', "VERTICAL TAB")] + [DataRow('\x000C', '\x240C', "FORM FEED")] + [DataRow('\r', '\x240D', "CARRIAGE RETURN")] + [DataRow('\x000E', '\x240E', "SHIFT OUT")] + [DataRow('\x000F', '\x240F', "SHIFT IN")] + [DataRow('\x0010', '\x2410', "DATA LINK ESCAPE")] + [DataRow('\x0011', '\x2411', "DEVICE CONTROL ONE")] + [DataRow('\x0012', '\x2412', "DEVICE CONTROL TWO")] + [DataRow('\x0013', '\x2413', "DEVICE CONTROL THREE")] + [DataRow('\x0014', '\x2414', "DEVICE CONTROL FOUR")] + [DataRow('\x0015', '\x2415', "NEGATIVE ACKNOWLEDGE")] + [DataRow('\x0016', '\x2416', "SYNCHRONOUS IDLE")] + [DataRow('\x0017', '\x2417', "END OF TRANSMISSION BLOCK")] + [DataRow('\x0018', '\x2418', "CANCEL")] + [DataRow('\x0019', '\x2419', "END OF MEDIUM")] + [DataRow('\x001A', '\x241A', "SUBSTITUTE")] + [DataRow('\x001B', '\x241B', "ESCAPE")] + [DataRow('\x001C', '\x241C', "FILE SEPARATOR")] + [DataRow('\x001D', '\x241D', "GROUP SEPARATOR")] + [DataRow('\x001E', '\x241E', "RECORD SEPARATOR")] + [DataRow('\x001F', '\x241F', "UNIT SEPARATOR")] + [TestMethod] + public void TestDiscovery_WithControlCharacters_AreNormalized(char controlChar, char expectedChar, string charName) + { + string targetFramework = "net8.0"; + string architecture = "x64"; + string assembly = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? @"C:\work\assembly.dll" : "/mnt/work/assembly.dll"; + + var stringBuilderConsole = new StringBuilderConsole(); + var terminalReporter = new TerminalTestReporter(assembly, targetFramework, architecture, stringBuilderConsole, new CTRLPlusCCancellationTokenSource(), new TerminalTestReporterOptions + { + ShowPassedTests = () => true, + UseAnsi = false, + ShowProgress = () => false, + }); + + DateTimeOffset startTime = DateTimeOffset.MinValue; + DateTimeOffset endTime = DateTimeOffset.MaxValue; + terminalReporter.TestExecutionStarted(startTime, 1, isDiscovery: true); + + terminalReporter.AssemblyRunStarted(); + + // Test discovery with the specific control character + string testDisplayName = $"Test{controlChar}Name"; + terminalReporter.TestDiscovered(testDisplayName); + + terminalReporter.AssemblyRunCompleted(); + terminalReporter.TestExecutionCompleted(endTime); + + string output = stringBuilderConsole.Output; + + // Verify that the control character is replaced with its Unicode control picture + string normalizedDisplayName = $"Test{expectedChar}Name"; + Assert.Contains(normalizedDisplayName, output, $"{charName} should be replaced with {expectedChar} in discovery"); + + // Verify that the literal control character is not present in the test display name + // Note: We skip this assertion for whitespace characters (\t, \n, \r) because these + // characters naturally appear in console output formatting (e.g., line breaks between tests) + // and asserting their complete absence would cause false positives + string literalDisplayName = $"Test{controlChar}Name"; + bool isWhitespaceChar = controlChar is '\t' or '\n' or '\r'; + if (!isWhitespaceChar) + { + Assert.DoesNotContain(literalDisplayName, output, $"Literal {charName} should not be present in test display name"); + } + } + + [TestMethod] + public void TestProgressState_WhenCreatedWithDiscoveryTrue_ShouldHaveIsDiscoveryTrue() + { + // Arrange + var stopwatch = new StopwatchFactory.MockStopwatch(new StopwatchFactory(), TimeSpan.Zero); + + // Act + var progressState = new TestProgressState(1, "test.dll", "net8.0", "x64", stopwatch, isDiscovery: true); + + // Assert + Assert.IsTrue(progressState.IsDiscovery); + Assert.AreEqual(0, progressState.DiscoveredTests); + } + + [TestMethod] + public void TestProgressState_WhenCreatedWithFalseIsDiscoveryParameter_ShouldHaveIsDiscoveryFalse() + { + // Arrange + var stopwatch = new StopwatchFactory.MockStopwatch(new StopwatchFactory(), TimeSpan.Zero); + + // Act + var progressState = new TestProgressState(1, "test.dll", "net8.0", "x64", stopwatch, isDiscovery: false); + + // Assert + Assert.IsFalse(progressState.IsDiscovery); + Assert.AreEqual(0, progressState.DiscoveredTests); + } + + [TestMethod] + public void TerminalTestReporter_WhenInDiscoveryMode_ShouldIncrementDiscoveredTests() + { + // Arrange + string assembly = "test.dll"; + var stringBuilderConsole = new StringBuilderConsole(); + var terminalReporter = new TerminalTestReporter(assembly, "net8.0", "x64", stringBuilderConsole, new CTRLPlusCCancellationTokenSource(), new TerminalTestReporterOptions + { + ShowPassedTests = () => false, + UseAnsi = false, + ShowProgress = () => false, + }); + + DateTimeOffset startTime = DateTimeOffset.MinValue; + DateTimeOffset endTime = DateTimeOffset.MaxValue; + + // Act + terminalReporter.TestExecutionStarted(startTime, 1, isDiscovery: true); + terminalReporter.AssemblyRunStarted(); + terminalReporter.TestDiscovered("TestMethod1"); + terminalReporter.TestDiscovered("TestMethod2"); + terminalReporter.AssemblyRunCompleted(); + terminalReporter.TestExecutionCompleted(endTime); + + string output = stringBuilderConsole.Output; + + // Assert - should contain information about 2 tests discovered + Assert.IsTrue(output.Contains('2') || output.Contains("TestMethod1"), "Output should contain information about discovered tests"); + } } diff --git a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Requests/TreeNodeFilterTests.cs b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Requests/TreeNodeFilterTests.cs index f587f02457..241cf0a1d9 100644 --- a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Requests/TreeNodeFilterTests.cs +++ b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Requests/TreeNodeFilterTests.cs @@ -3,8 +3,8 @@ using Microsoft.Testing.Platform.Extensions.Messages; using Microsoft.Testing.Platform.Requests; +#pragma warning disable CS0618 // Type or member is obsolete -#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; [TestClass] @@ -26,10 +26,10 @@ public void MatchAllFilter_MatchesSubpaths() } [TestMethod] - public void MatchAllFilter_Invalid() => Assert.ThrowsException(() => _ = new TreeNodeFilter("/A(&B)")); + public void MatchAllFilter_Invalid() => Assert.ThrowsExactly(() => _ = new TreeNodeFilter("/A(&B)")); [TestMethod] - public void MatchAllFilter_DoNotAllowInMiddleOfFilter() => Assert.ThrowsException(() => _ = new TreeNodeFilter("/**/Path")); + public void MatchAllFilter_DoNotAllowInMiddleOfFilter() => Assert.ThrowsExactly(() => _ = new TreeNodeFilter("/**/Path")); [TestMethod] public void MatchWildcard_MatchesSubstrings() @@ -59,7 +59,7 @@ public void EscapeSequences_SupportsParentheses() } [TestMethod] - public void EscapeSequences_ThrowsIfLastCharIsAnEscapeChar() => Assert.ThrowsException(() => _ = new TreeNodeFilter("/*.\\(UnitTests\\)\\")); + public void EscapeSequences_ThrowsIfLastCharIsAnEscapeChar() => Assert.ThrowsExactly(() => _ = new TreeNodeFilter("/*.\\(UnitTests\\)\\")); [TestMethod] public void OrExpression_WorksForLiteralStrings() @@ -80,6 +80,112 @@ public void AndExpression() Assert.IsFalse(filter.MatchesFilter("/ProjectC.UnitTests.SomeExtension", new PropertyBag())); } + [TestMethod] + public void NotExpression_DisallowSuffix() + { + TreeNodeFilter filter = new("/(!*UnitTests)"); + Assert.IsFalse(filter.MatchesFilter("/A.UnitTests", new PropertyBag())); + Assert.IsFalse(filter.MatchesFilter("/UnitTests", new PropertyBag())); + Assert.IsTrue(filter.MatchesFilter("/A", new PropertyBag())); + Assert.IsTrue(filter.MatchesFilter("/UnitTests.A", new PropertyBag())); + } + + [TestMethod] + public void NotExpression_DisallowPrefix() + { + TreeNodeFilter filter = new("/(!UnitTests*)"); + Assert.IsFalse(filter.MatchesFilter("/UnitTests.A", new PropertyBag())); + Assert.IsFalse(filter.MatchesFilter("/UnitTests", new PropertyBag())); + Assert.IsTrue(filter.MatchesFilter("/A", new PropertyBag())); + Assert.IsTrue(filter.MatchesFilter("/A.UnitTests", new PropertyBag())); + } + + [TestMethod] + public void NotExpression_DisallowContains() + { + TreeNodeFilter filter = new("/(!*UnitTests*)"); + Assert.IsFalse(filter.MatchesFilter("/UnitTests.A", new PropertyBag())); + Assert.IsFalse(filter.MatchesFilter("/A.UnitTests", new PropertyBag())); + Assert.IsFalse(filter.MatchesFilter("/UnitTests", new PropertyBag())); + Assert.IsTrue(filter.MatchesFilter("/A", new PropertyBag())); + } + + [TestMethod] + public void NotExpression_CombinedWithAND_Parenthesized() + { + // Matches anything except A*Z + TreeNodeFilter filter = new("/(!(A*&*Z))"); + Assert.IsFalse(filter.MatchesFilter("/AZ", new PropertyBag())); // !(true && true) ==> false + Assert.IsFalse(filter.MatchesFilter("/ABCZ", new PropertyBag())); // !(true && true) ==> false + Assert.IsTrue(filter.MatchesFilter("/C", new PropertyBag())); // !(false && false) ==> true + Assert.IsTrue(filter.MatchesFilter("/A", new PropertyBag())); // !(true && false) ==> true + Assert.IsTrue(filter.MatchesFilter("/ABC", new PropertyBag())); // !(true && false) ==> true + Assert.IsTrue(filter.MatchesFilter("/Z", new PropertyBag())); // !(false && true) ==> true + Assert.IsTrue(filter.MatchesFilter("/XYZ", new PropertyBag())); // !(false && true) ==> true + } + + [TestMethod] + public void NotExpression_CombinedWithOR_Parenthesized() + { + // Doesn't match A*, and also doesn't match *Z + TreeNodeFilter filter = new("/(!(A*|*Z))"); + Assert.IsFalse(filter.MatchesFilter("/AZ", new PropertyBag())); // !(true || true) ==> false + Assert.IsFalse(filter.MatchesFilter("/AB", new PropertyBag())); // !(true || false) ==> false + Assert.IsFalse(filter.MatchesFilter("/A", new PropertyBag())); // !(true || true) ==> false + Assert.IsFalse(filter.MatchesFilter("/ABZ", new PropertyBag())); // !(true || true) ==> false + Assert.IsFalse(filter.MatchesFilter("/YZ", new PropertyBag())); // !(false || true) ==> false + Assert.IsFalse(filter.MatchesFilter("/Z", new PropertyBag())); // !(false || true) ==> false + + Assert.IsTrue(filter.MatchesFilter("/C", new PropertyBag())); // !(false || false) ==> true + Assert.IsTrue(filter.MatchesFilter("/CA", new PropertyBag())); // !(false || false) ==> true + Assert.IsTrue(filter.MatchesFilter("/ZS", new PropertyBag())); // !(false || false) ==> true + Assert.IsTrue(filter.MatchesFilter("/ZA", new PropertyBag())); // !(false || false) ==> true + Assert.IsTrue(filter.MatchesFilter("/ZYYA", new PropertyBag())); // !(false || false) ==> true + } + + [TestMethod] + public void NotExpression_CombinedWithAND_NotParenthesized() + { + // Matches anything that doesn't start with A, but should end with Z + TreeNodeFilter filter = new("/(!A*&*Z)"); + + // Cases not ending with Z, filter doesn't match. + Assert.IsFalse(filter.MatchesFilter("/A", new PropertyBag())); + Assert.IsFalse(filter.MatchesFilter("/ZA", new PropertyBag())); + Assert.IsFalse(filter.MatchesFilter("/AZA", new PropertyBag())); + + // Cases ending with Z but starts with A. Filter shouldn't match. + Assert.IsFalse(filter.MatchesFilter("/AZ", new PropertyBag())); + Assert.IsFalse(filter.MatchesFilter("/ABZ", new PropertyBag())); + + // Cases ending with Z and don't start with A. Filter should match. + Assert.IsTrue(filter.MatchesFilter("/BAZ", new PropertyBag())); + Assert.IsTrue(filter.MatchesFilter("/BZ", new PropertyBag())); + } + + [TestMethod] + public void NotExpression_CombinedWithOR_NotParenthesized() + { + // Matches anything that either doesn't start with A, or ends with Z + TreeNodeFilter filter = new("/(!A*|*Z)"); + + // Cases not starting with A + Assert.IsTrue(filter.MatchesFilter("/Y", new PropertyBag())); + Assert.IsTrue(filter.MatchesFilter("/Z", new PropertyBag())); + Assert.IsTrue(filter.MatchesFilter("/ZA", new PropertyBag())); + Assert.IsTrue(filter.MatchesFilter("/ZAZ", new PropertyBag())); + Assert.IsTrue(filter.MatchesFilter("/YAZ", new PropertyBag())); + + // Cases starting with A, and ending with Z + Assert.IsTrue(filter.MatchesFilter("/AZ", new PropertyBag())); + Assert.IsTrue(filter.MatchesFilter("/ABZ", new PropertyBag())); + + // Cases starting with A, and not ending with Z + Assert.IsFalse(filter.MatchesFilter("/A", new PropertyBag())); + Assert.IsFalse(filter.MatchesFilter("/AB", new PropertyBag())); + Assert.IsFalse(filter.MatchesFilter("/AZB", new PropertyBag())); + } + [TestMethod] public void Parentheses_EnsuresOrdering() { @@ -94,7 +200,7 @@ public void Parentheses_EnsuresOrdering() [TestMethod] public void Parenthesis_DisallowSeparatorInside() - => Assert.ThrowsException(() => new TreeNodeFilter("/(A/B)")); + => Assert.ThrowsExactly(() => new TreeNodeFilter("/(A/B)")); [TestMethod] public void Parameters_PropertyCheck() @@ -143,19 +249,19 @@ public void Parameters_NegatedPropertyCheckCombinedWithOr() [TestMethod] public void Parameters_DisallowAtStart() - => Assert.ThrowsException(() => _ = new TreeNodeFilter("/[Tag=Fast]")); + => Assert.ThrowsExactly(() => _ = new TreeNodeFilter("/[Tag=Fast]")); [TestMethod] public void Parameters_DisallowEmpty() - => Assert.ThrowsException(() => _ = new TreeNodeFilter("/Path[]")); + => Assert.ThrowsExactly(() => _ = new TreeNodeFilter("/Path[]")); [TestMethod] public void Parameters_DisallowMultiple() - => Assert.ThrowsException(() => _ = new TreeNodeFilter("/Path[Prop=2][Prop=B]")); + => Assert.ThrowsExactly(() => _ = new TreeNodeFilter("/Path[Prop=2][Prop=B]")); [TestMethod] public void Parameters_DisallowNested() - => Assert.ThrowsException(() => _ = new TreeNodeFilter("/Path[X=[Y=1]]")); + => Assert.ThrowsExactly(() => _ = new TreeNodeFilter("/Path[X=[Y=1]]")); [DataRow("/A/B", "/A/B", true)] [DataRow("/A/B", "/A%2FB", false)] @@ -221,5 +327,13 @@ public void MatchAllFilterSubpathWithPropertyExpression() } [TestMethod] - public void MatchAllFilterWithPropertyExpression_DoNotAllowInMiddleOfFilter() => Assert.ThrowsException(() => _ = new TreeNodeFilter("/**/Path[A=B]")); + public void MatchAllFilterSubpathWithPropertyExpression_WithTestMetadataProperty() + { + TreeNodeFilter filter = new("/A/**[A=B]"); + Assert.IsTrue(filter.MatchesFilter("/A/B/C/D", new PropertyBag(new TestMetadataProperty("A", "B")))); + Assert.IsFalse(filter.MatchesFilter("/B/A/C/D", new PropertyBag(new TestMetadataProperty("A", "B")))); + } + + [TestMethod] + public void MatchAllFilterWithPropertyExpression_DoNotAllowInMiddleOfFilter() => Assert.ThrowsExactly(() => _ = new TreeNodeFilter("/**/Path[A=B]")); } diff --git a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/ServerMode/FormatterUtilitiesTests.cs b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/ServerMode/FormatterUtilitiesTests.cs index 75b6c6ee89..1ebac94ab6 100644 --- a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/ServerMode/FormatterUtilitiesTests.cs +++ b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/ServerMode/FormatterUtilitiesTests.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. -#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. - using Microsoft.Testing.Platform.Extensions.Messages; using Microsoft.Testing.Platform.ServerMode; @@ -176,7 +174,7 @@ private static void AssertSerialize(Type type, string instanceSerialized) if (type == typeof(TestNode)) { - Assert.AreEqual("""{"uid":"uid","display-name":"DisplayName","traits":[{"testmetadata-key":"testmetadata-value"}],"standardError":"textProperty2","standardOutput":"textProperty","time.start-utc":"2023-01-01T01:01:01.0000000+00:00","time.stop-utc":"2023-01-01T01:01:01.0000000+00:00","time.duration-ms":0,"location.type":"namespace.typeName","location.method":"methodName(param1,param2)","location.method-arity":0,"location.file":"filePath","location.line-start":1,"location.line-end":2,"key":"value","node-type":"action","execution-state":"failed","error.message":"sample","error.stacktrace":"","assert.actual":"","assert.expected":""}""".Replace(" ", string.Empty), instanceSerialized, because); + Assert.AreEqual("""{"uid":"uid","display-name":"DisplayName","traits":[{"testmetadata-key":"testmetadata-value"}],"standardError":"textProperty2","standardOutput":"textProperty","time.duration-ms":0,"location.type":"namespace.typeName","location.method":"methodName(param1,param2)","location.method-arity":0,"location.file":"filePath","location.line-start":1,"location.line-end":2,"key":"value","node-type":"action","execution-state":"failed","error.message":"sample","error.stacktrace":"","assert.actual":"","assert.expected":""}""".Replace(" ", string.Empty), instanceSerialized, because); return; } @@ -188,7 +186,7 @@ private static void AssertSerialize(Type type, string instanceSerialized) if (type == typeof(TestNodeUpdateMessage)) { - Assert.AreEqual("""{"node":{"uid":"uid","display-name":"DisplayName","traits":[{"testmetadata-key":"testmetadata-value"}],"standardError":"textProperty2","standardOutput":"textProperty","time.start-utc":"2023-01-01T01:01:01.0000000+00:00","time.stop-utc":"2023-01-01T01:01:01.0000000+00:00","time.duration-ms":0,"location.type":"namespace.typeName","location.method":"methodName(param1,param2)","location.method-arity":0,"location.file":"filePath","location.line-start":1,"location.line-end":2,"key":"value","node-type":"action","execution-state":"failed","error.message":"sample","error.stacktrace":"","assert.actual":"","assert.expected":""},"parent":"parent-uid"}""".Replace(" ", string.Empty), instanceSerialized, because); + Assert.AreEqual("""{"node":{"uid":"uid","display-name":"DisplayName","traits":[{"testmetadata-key":"testmetadata-value"}],"standardError":"textProperty2","standardOutput":"textProperty","time.duration-ms":0,"location.type":"namespace.typeName","location.method":"methodName(param1,param2)","location.method-arity":0,"location.file":"filePath","location.line-start":1,"location.line-end":2,"key":"value","node-type":"action","execution-state":"failed","error.message":"sample","error.stacktrace":"","assert.actual":"","assert.expected":""},"parent":"parent-uid"}""".Replace(" ", string.Empty), instanceSerialized, because); return; } @@ -363,7 +361,7 @@ private static object CreateInstance(Type type) if (type == typeof(TestsAttachments)) { - return new TestsAttachments(new RunTestAttachment[] { new("Uri", "Producer", "Type", "DisplayName", "Description") }); + return new TestsAttachments([new("Uri", "Producer", "Type", "DisplayName", "Description")]); } if (type == typeof(RunTestAttachment)) diff --git a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/ServerMode/JsonTests.cs b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/ServerMode/JsonTests.cs index cfa9e68ac5..f3e0ffd468 100644 --- a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/ServerMode/JsonTests.cs +++ b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/ServerMode/JsonTests.cs @@ -16,13 +16,13 @@ public sealed class JsonTests public JsonTests() { - Dictionary serializers = new(); - Dictionary deserializers = new(); + Dictionary serializers = []; + Dictionary deserializers = []; foreach (Type serializableType in SerializerUtilities.SerializerTypes) { serializers[serializableType] = new JsonObjectSerializer( - o => SerializerUtilities.Serialize(serializableType, o).Select(kvp => (kvp.Key, kvp.Value)).ToArray()); + o => [.. SerializerUtilities.Serialize(serializableType, o).Select(kvp => (kvp.Key, kvp.Value))]); } foreach (Type deserializableType in SerializerUtilities.DeserializerTypes) @@ -125,43 +125,14 @@ public void DeserializePerson() [typeof(Person)] = new JsonElementDeserializer((json, jsonElement) => new Person { Name = json.Bind(jsonElement, "name"), - Children = json.Bind>(jsonElement, "children"), }), - - [typeof(List)] = new JsonCollectionDeserializer, Person>(_ => [], (c, i) => c.Add(i)), }); // Act - Person actual = json.Deserialize(new("""{"name":"Thomas","children":[{"name":"Ruth","children":null}]}""".ToCharArray())); + Person actual = json.Deserialize(new("""{"name":"Thomas"}""".ToCharArray())); // Assert Assert.AreEqual("Thomas", actual.Name); - Assert.AreEqual(1, actual.Children!.Count); - Assert.AreEqual("Ruth", actual.Children![0].Name); - Assert.IsNull(actual.Children![0].Children); - } - - [TestMethod] - public void DeserializePersonList() - { - // Arrange - Json json = new(null, new Dictionary - { - [typeof(Person)] = new JsonElementDeserializer((json, jsonElement) => new Person - { - Name = json.Bind(jsonElement, "name"), - Children = json.Bind>(jsonElement, "children"), - }), - - [typeof(List)] = new JsonCollectionDeserializer, Person>(_ => [], (c, i) => c.Add(i)), - }); - - // Act - List actual = json.Deserialize>(new("""[{"name":"Thomas","children":[{"name":"Ruth","children":null}]}]""".ToCharArray())); - - // Assert - Assert.AreEqual(1, actual.Count); - Assert.AreEqual("Thomas", actual[0].Name); } private sealed class Person diff --git a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/ServerMode/JsoniteTests.cs b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/ServerMode/JsoniteTests.cs index d2aa862511..ebc6fac83b 100644 --- a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/ServerMode/JsoniteTests.cs +++ b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/ServerMode/JsoniteTests.cs @@ -22,7 +22,7 @@ public void Serialize_SpecialCharacters() { // This test is testing if we can serialize the range 0x0000 - 0x001FF correctly, this range contains special characters like NUL. // This is a fix for Jsonite, which throws when such characters are found in a string (but does not fail when we provide them as character). - List errors = new(); + List errors = []; // This could be converted to Data source, but this way we have more control about where in the result message the // special characters will be (hopefully nowhere) so in case of failure, we can still serialize the message to IDE diff --git a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/ServerMode/ServerDataConsumerServiceTests.cs b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/ServerMode/ServerDataConsumerServiceTests.cs index e1703f6e8d..8fe8ab2684 100644 --- a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/ServerMode/ServerDataConsumerServiceTests.cs +++ b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/ServerMode/ServerDataConsumerServiceTests.cs @@ -64,7 +64,7 @@ public async Task ConsumeAsync_WithTestNodeUpdatedMessage() await _service.ConsumeAsync(producer, testNode, CancellationToken.None).ConfigureAwait(false); List actual = _service.Artifacts; - Assert.AreEqual(0, actual.Count); + Assert.IsEmpty(actual); } [TestMethod] diff --git a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/ServerMode/ServerTests.cs b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/ServerMode/ServerTests.cs index 6d14e1a477..f3c215d8c8 100644 --- a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/ServerMode/ServerTests.cs +++ b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/ServerMode/ServerTests.cs @@ -38,7 +38,7 @@ public async Task ServerCanBeStartedAndAborted_TcpIp() => await RetryHelper.Retr TestApplicationHooks testApplicationHooks = new(); string[] args = ["--no-banner", "--server", "--client-host", "localhost", "--client-port", $"{server.Port}", "--internal-testingplatform-skipbuildercheck"]; ITestApplicationBuilder builder = await TestApplication.CreateBuilderAsync(args); - builder.TestHost.AddTestApplicationLifecycleCallbacks(_ => testApplicationHooks); + builder.TestHost.AddTestHostApplicationLifetime(_ => testApplicationHooks); builder.RegisterTestFramework(_ => new TestFrameworkCapabilities(), (_, __) => new MockTestAdapter()); var testApplication = (TestApplication)await builder.BuildAsync(); testApplication.ServiceProvider.GetRequiredService().SuppressOutput(); @@ -59,7 +59,7 @@ public async Task ServerCanInitialize() string[] args = ["--no-banner", "--server", "--client-port", $"{server.Port}", "--internal-testingplatform-skipbuildercheck"]; TestApplicationHooks testApplicationHooks = new(); ITestApplicationBuilder builder = await TestApplication.CreateBuilderAsync(args); - builder.TestHost.AddTestApplicationLifecycleCallbacks(_ => testApplicationHooks); + builder.TestHost.AddTestHostApplicationLifetime(_ => testApplicationHooks); builder.RegisterTestFramework(_ => new TestFrameworkCapabilities(), (_, __) => new MockTestAdapter()); var testApplication = (TestApplication)await builder.BuildAsync(); testApplication.ServiceProvider.GetRequiredService().SuppressOutput(); @@ -252,7 +252,7 @@ private static async Task WriteMessageAsync(StreamWriter writer, string message) await writer.FlushAsync(); } - private sealed class TestApplicationHooks : ITestApplicationLifecycleCallbacks, IDisposable + private sealed class TestApplicationHooks : ITestHostApplicationLifetime, IDisposable { private readonly SemaphoreSlim _waitForBeforeRunAsync = new(0, 1); diff --git a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Services/ServiceProviderTests.cs b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Services/ServiceProviderTests.cs index 1ad71b8e80..330c943b49 100644 --- a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Services/ServiceProviderTests.cs +++ b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Services/ServiceProviderTests.cs @@ -32,7 +32,10 @@ public void GetService_InternalExtension_ShouldNotReturn() Assert.IsNull(_serviceProvider.GetService()); _serviceProvider.AddService(new TestApplicationLifecycleCallbacks()); +#pragma warning disable CS0618 // Type or member is obsolete Assert.IsNull(_serviceProvider.GetService()); +#pragma warning restore CS0618 // Type or member is obsolete + Assert.IsNull(_serviceProvider.GetService()); } [TestMethod] @@ -51,7 +54,10 @@ public void GetServiceInternal_InternalExtension_ShouldReturn() Assert.IsNotNull(_serviceProvider.GetServiceInternal()); _serviceProvider.AddService(new TestApplicationLifecycleCallbacks()); +#pragma warning disable CS0618 // Type or member is obsolete Assert.IsNotNull(_serviceProvider.GetServiceInternal()); +#pragma warning restore CS0618 // Type or member is obsolete + Assert.IsNotNull(_serviceProvider.GetServiceInternal()); } [TestMethod] @@ -65,7 +71,7 @@ public void Clone_WithoutFilter_Succeeded() var clonedServiceProvider = (ServiceProvider)_serviceProvider.Clone(); - Assert.AreEqual(_serviceProvider.Services.Count, clonedServiceProvider.Services.Count); + Assert.HasCount(_serviceProvider.Services.Count, clonedServiceProvider.Services); for (int i = 0; i < _serviceProvider.Services.Count; i++) { Assert.AreEqual(_serviceProvider.Services.ToArray()[i], clonedServiceProvider.Services.ToArray()[i]); @@ -83,17 +89,17 @@ public void Clone_WithFilter_Succeeded() var clonedServiceProvider = (ServiceProvider)_serviceProvider.Clone(o => o is TestHostProcessLifetimeHandler); - Assert.AreEqual(1, clonedServiceProvider.Services.Count); + Assert.HasCount(1, clonedServiceProvider.Services); Assert.AreEqual(_serviceProvider.Services.ToArray()[0].GetType(), typeof(TestHostProcessLifetimeHandler)); } [TestMethod] public void AddService_TestFramework_ShouldFail() - => Assert.ThrowsException(() => _serviceProvider.AddService(new TestFramework())); + => Assert.ThrowsExactly(() => _serviceProvider.AddService(new TestFramework())); [TestMethod] public void TryAddService_TestFramework_ShouldFail() - => Assert.ThrowsException(() => _serviceProvider.TryAddService(new TestFramework())); + => Assert.ThrowsExactly(() => _serviceProvider.TryAddService(new TestFramework())); [TestMethod] public void AddService_TestFramework_ShouldNotFail() @@ -114,7 +120,7 @@ public void AddService_SameInstance_ShouldFail() { TestHostProcessLifetimeHandler instance = new(); _serviceProvider.AddService(instance); - _ = Assert.ThrowsException(() => _serviceProvider.AddService(instance)); + _ = Assert.ThrowsExactly(() => _serviceProvider.AddService(instance)); } [TestMethod] @@ -130,7 +136,7 @@ public void AddServices_SameInstance_ShouldFail() { TestHostProcessLifetimeHandler instance = new(); _serviceProvider.AddServices([instance]); - _ = Assert.ThrowsException(() => _serviceProvider.AddServices([instance])); + _ = Assert.ThrowsExactly(() => _serviceProvider.AddServices([instance])); } [TestMethod] @@ -155,7 +161,10 @@ public void GetServicesInternal_ExtensionMethod_InternalExtension_ShouldReturn() _serviceProvider.AddService(new TestApplicationLifecycleCallbacks()); _serviceProvider.AddService(new TestApplicationLifecycleCallbacks()); +#pragma warning disable CS0618 // Type or member is obsolete Assert.AreEqual(2, _serviceProvider.GetServicesInternal().Count()); +#pragma warning restore CS0618 // Type or member is obsolete + Assert.AreEqual(2, _serviceProvider.GetServicesInternal().Count()); } [TestMethod] @@ -164,7 +173,10 @@ public void GetServicesInternal_InternalExtension_ShouldNotReturn() _serviceProvider.AddService(new TestApplicationLifecycleCallbacks()); _serviceProvider.AddService(new TestApplicationLifecycleCallbacks()); +#pragma warning disable CS0618 // Type or member is obsolete Assert.AreEqual(0, _serviceProvider.GetServicesInternal(typeof(ITestApplicationLifecycleCallbacks), stopAtFirst: false, skipInternalOnlyExtensions: true).Count()); +#pragma warning restore CS0618 // Type or member is obsolete + Assert.AreEqual(0, _serviceProvider.GetServicesInternal(typeof(ITestHostApplicationLifetime), stopAtFirst: false, skipInternalOnlyExtensions: true).Count()); } [TestMethod] @@ -173,7 +185,10 @@ public void GetServicesInternal_InternalExtension_ShouldReturn() _serviceProvider.AddService(new TestApplicationLifecycleCallbacks()); _serviceProvider.AddService(new TestApplicationLifecycleCallbacks()); +#pragma warning disable CS0618 // Type or member is obsolete Assert.AreEqual(2, _serviceProvider.GetServicesInternal(typeof(ITestApplicationLifecycleCallbacks), stopAtFirst: false, skipInternalOnlyExtensions: false).Count()); +#pragma warning restore CS0618 // Type or member is obsolete + Assert.AreEqual(2, _serviceProvider.GetServicesInternal(typeof(ITestHostApplicationLifetime), stopAtFirst: false, skipInternalOnlyExtensions: false).Count()); } [TestMethod] @@ -182,7 +197,10 @@ public void GetServicesInternal_InternalExtension_FirstOnly_ShouldReturnOne() _serviceProvider.AddService(new TestApplicationLifecycleCallbacks()); _serviceProvider.AddService(new TestApplicationLifecycleCallbacks()); +#pragma warning disable CS0618 // Type or member is obsolete Assert.AreEqual(1, _serviceProvider.GetServicesInternal(typeof(ITestApplicationLifecycleCallbacks), stopAtFirst: true, skipInternalOnlyExtensions: false).Count()); +#pragma warning restore CS0618 // Type or member is obsolete + Assert.AreEqual(1, _serviceProvider.GetServicesInternal(typeof(ITestHostApplicationLifetime), stopAtFirst: true, skipInternalOnlyExtensions: false).Count()); } [TestMethod] @@ -191,7 +209,10 @@ public void GetServiceInternal_InternalExtension_ShouldReturnOne() _serviceProvider.AddService(new TestApplicationLifecycleCallbacks()); _serviceProvider.AddService(new TestApplicationLifecycleCallbacks()); +#pragma warning disable CS0618 // Type or member is obsolete Assert.IsNotNull(_serviceProvider.GetServiceInternal(typeof(ITestApplicationLifecycleCallbacks), skipInternalOnlyExtensions: false)); +#pragma warning restore CS0618 // Type or member is obsolete + Assert.IsNotNull(_serviceProvider.GetServiceInternal(typeof(ITestHostApplicationLifetime), skipInternalOnlyExtensions: false)); } [TestMethod] @@ -200,7 +221,10 @@ public void GetServiceInternal_InternalExtension_SkipInternalOnlyExtensions_Shou _serviceProvider.AddService(new TestApplicationLifecycleCallbacks()); _serviceProvider.AddService(new TestApplicationLifecycleCallbacks()); +#pragma warning disable CS0618 // Type or member is obsolete Assert.IsNull(_serviceProvider.GetServiceInternal(typeof(ITestApplicationLifecycleCallbacks), skipInternalOnlyExtensions: true)); +#pragma warning restore CS0618 // Type or member is obsolete + Assert.IsNull(_serviceProvider.GetServiceInternal(typeof(ITestHostApplicationLifetime), skipInternalOnlyExtensions: true)); } private sealed class TestFramework : ITestFramework @@ -294,7 +318,7 @@ private sealed class DataConsumer : IDataConsumer public Task IsEnabledAsync() => throw new NotImplementedException(); } - private sealed class TestApplicationLifecycleCallbacks : ITestApplicationLifecycleCallbacks + private sealed class TestApplicationLifecycleCallbacks : ITestHostApplicationLifetime { public string Uid => throw new NotImplementedException(); diff --git a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Services/TestApplicationResultTests.cs b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Services/TestApplicationResultTests.cs index 580fd28310..1d74be935a 100644 --- a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Services/TestApplicationResultTests.cs +++ b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Services/TestApplicationResultTests.cs @@ -214,10 +214,10 @@ public void GetProcessExitCodeAsync_IgnoreExitCodes(string? argument, int expect internal static IEnumerable FailedState() { - yield return new[] { new FailedTestNodeStateProperty() }; - yield return new[] { new ErrorTestNodeStateProperty() }; - yield return new[] { new CancelledTestNodeStateProperty() }; - yield return new[] { new TimeoutTestNodeStateProperty() }; + yield return [new FailedTestNodeStateProperty()]; + yield return [new ErrorTestNodeStateProperty()]; + yield return [new CancelledTestNodeStateProperty()]; + yield return [new TimeoutTestNodeStateProperty()]; } private sealed class CommandLineOption : ICommandLineOptions diff --git a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Telemetry/TelemetryManagerTests.cs b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Telemetry/TelemetryManagerTests.cs index 763032a277..fd54131f0a 100644 --- a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Telemetry/TelemetryManagerTests.cs +++ b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Telemetry/TelemetryManagerTests.cs @@ -169,7 +169,7 @@ public async Task TelemetryManager_SentinelIsWrittenPerUserAndAvoidsShowingNotic // Combination of where LOCALAPPDATA or HOME is, the name of the exe and our file extension. string path = Path.Combine("sentinelDir", "Microsoft", "TestingPlatform", "myExe.testingPlatformFirstTimeUseSentinel"); - fileSystemMock.Verify(f => f.Exists(path), Times.Once); + fileSystemMock.Verify(f => f.ExistFile(path), Times.Once); // Message was written to screen. outputDevice.Verify(c => c.DisplayAsync(It.IsAny(), It.IsAny()), Times.Once); @@ -181,9 +181,9 @@ public async Task TelemetryManager_SentinelIsWrittenPerUserAndAvoidsShowingNotic outputDevice.Invocations.Clear(); fileSystemMock.Invocations.Clear(); - fileSystemMock.Setup(f => f.Exists(path)).Returns(true); + fileSystemMock.Setup(f => f.ExistFile(path)).Returns(true); await telemetryManager.BuildAsync(serviceProvider, loggerFactoryMock.Object, options); - fileSystemMock.Verify(f => f.Exists(path), Times.Once); + fileSystemMock.Verify(f => f.ExistFile(path), Times.Once); // Message is not written to screen. outputDevice.Verify(c => c.DisplayAsync(It.IsAny(), It.IsAny()), Times.Never); @@ -237,7 +237,7 @@ public async Task TelemetryManager_SentinelIsWrittenOnlyWhenUserWouldSeeTheMessa string path = Path.Combine("sentinelDir", "Microsoft", "TestingPlatform", "myExe.testingPlatformFirstTimeUseSentinel"); // We should not check for the sentinel, because we disabled the logo. - fileSystemMock.Verify(f => f.Exists(path), Times.Never); + fileSystemMock.Verify(f => f.ExistFile(path), Times.Never); // Message was not written to screen. outputDevice.Verify(c => c.DisplayAsync(It.IsAny(), It.IsAny()), Times.Never); @@ -252,9 +252,9 @@ public async Task TelemetryManager_SentinelIsWrittenOnlyWhenUserWouldSeeTheMessa // Enable showing the telemetry message. environmentMock.Setup(s => s.GetEnvironmentVariable(EnvironmentVariableConstants.TESTINGPLATFORM_NOBANNER)).Returns("0"); - fileSystemMock.Setup(f => f.Exists(path)).Returns(false); + fileSystemMock.Setup(f => f.ExistFile(path)).Returns(false); await telemetryManager.BuildAsync(serviceProvider, loggerFactoryMock.Object, options); - fileSystemMock.Verify(f => f.Exists(path), Times.Once); + fileSystemMock.Verify(f => f.ExistFile(path), Times.Once); // Message is written to screen. outputDevice.Verify(c => c.DisplayAsync(It.IsAny(), It.IsAny()), Times.Once); diff --git a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/TestApplicationBuilderTests.cs b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/TestApplicationBuilderTests.cs index d35d3d8e62..48445513ee 100644 --- a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/TestApplicationBuilderTests.cs +++ b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/TestApplicationBuilderTests.cs @@ -30,9 +30,9 @@ public TestApplicationBuilderTests() public async Task TestApplicationLifecycleCallbacks_DuplicatedId_ShouldFail() { TestHostManager testHostManager = new(); - testHostManager.AddTestApplicationLifecycleCallbacks(_ => new ApplicationLifecycleCallbacks("duplicatedId")); - testHostManager.AddTestApplicationLifecycleCallbacks(_ => new ApplicationLifecycleCallbacks("duplicatedId")); - InvalidOperationException invalidOperationException = await Assert.ThrowsExceptionAsync(() => testHostManager.BuildTestApplicationLifecycleCallbackAsync(_serviceProvider)); + testHostManager.AddTestHostApplicationLifetime(_ => new ApplicationLifecycleCallbacks("duplicatedId")); + testHostManager.AddTestHostApplicationLifetime(_ => new ApplicationLifecycleCallbacks("duplicatedId")); + InvalidOperationException invalidOperationException = await Assert.ThrowsExactlyAsync(() => testHostManager.BuildTestApplicationLifecycleCallbackAsync(_serviceProvider)); Assert.IsTrue(invalidOperationException.Message.Contains("duplicatedId") && invalidOperationException.Message.Contains(typeof(ApplicationLifecycleCallbacks).ToString())); } @@ -42,7 +42,7 @@ public async Task DataConsumer_DuplicatedId_ShouldFail() TestHostManager testHostManager = new(); testHostManager.AddDataConsumer(_ => new Consumer("duplicatedId")); testHostManager.AddDataConsumer(_ => new Consumer("duplicatedId")); - InvalidOperationException invalidOperationException = await Assert.ThrowsExceptionAsync(() => testHostManager.BuildDataConsumersAsync(_serviceProvider, [])); + InvalidOperationException invalidOperationException = await Assert.ThrowsExactlyAsync(() => testHostManager.BuildDataConsumersAsync(_serviceProvider, [])); Assert.IsTrue(invalidOperationException.Message.Contains("duplicatedId") && invalidOperationException.Message.Contains(typeof(Consumer).ToString())); } @@ -53,7 +53,7 @@ public async Task DataConsumer_DuplicatedIdWithCompositeFactory_ShouldFail() CompositeExtensionFactory compositeExtensionFactory = new(() => new Consumer("duplicatedId")); testHostManager.AddDataConsumer(_ => new Consumer("duplicatedId")); testHostManager.AddDataConsumer(compositeExtensionFactory); - InvalidOperationException invalidOperationException = await Assert.ThrowsExceptionAsync(() => testHostManager.BuildDataConsumersAsync(_serviceProvider, [])); + InvalidOperationException invalidOperationException = await Assert.ThrowsExactlyAsync(() => testHostManager.BuildDataConsumersAsync(_serviceProvider, [])); Assert.IsTrue(invalidOperationException.Message.Contains("duplicatedId") && invalidOperationException.Message.Contains(typeof(Consumer).ToString())); } @@ -63,7 +63,7 @@ public async Task TestSessionLifetimeHandle_DuplicatedId_ShouldFail() TestHostManager testHostManager = new(); testHostManager.AddTestSessionLifetimeHandle(_ => new TestSessionLifetimeHandler("duplicatedId")); testHostManager.AddTestSessionLifetimeHandle(_ => new TestSessionLifetimeHandler("duplicatedId")); - InvalidOperationException invalidOperationException = await Assert.ThrowsExceptionAsync(() => testHostManager.BuildTestSessionLifetimeHandleAsync(_serviceProvider, [])); + InvalidOperationException invalidOperationException = await Assert.ThrowsExactlyAsync(() => testHostManager.BuildTestSessionLifetimeHandleAsync(_serviceProvider, [])); Assert.IsTrue(invalidOperationException.Message.Contains("duplicatedId") && invalidOperationException.Message.Contains(typeof(TestSessionLifetimeHandler).ToString())); } @@ -74,7 +74,7 @@ public async Task TestSessionLifetimeHandle_DuplicatedIdWithCompositeFactory_Sho CompositeExtensionFactory compositeExtensionFactory = new(() => new TestSessionLifetimeHandler("duplicatedId")); testHostManager.AddTestSessionLifetimeHandle(_ => new TestSessionLifetimeHandler("duplicatedId")); testHostManager.AddTestSessionLifetimeHandle(compositeExtensionFactory); - InvalidOperationException invalidOperationException = await Assert.ThrowsExceptionAsync(() => testHostManager.BuildTestSessionLifetimeHandleAsync(_serviceProvider, [])); + InvalidOperationException invalidOperationException = await Assert.ThrowsExactlyAsync(() => testHostManager.BuildTestSessionLifetimeHandleAsync(_serviceProvider, [])); Assert.IsTrue(invalidOperationException.Message.Contains("duplicatedId") && invalidOperationException.Message.Contains(typeof(TestSessionLifetimeHandler).ToString())); } @@ -91,10 +91,10 @@ public async Task TestHost_ComposeFactory_ShouldSucceed(bool withParameter) testHostManager.AddTestSessionLifetimeHandle(compositeExtensionFactory); testHostManager.AddDataConsumer(compositeExtensionFactory); 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); - Assert.AreEqual(1, sessionLifetimeHandle.Length); + IDataConsumer[] consumers = [.. (await testHostManager.BuildDataConsumersAsync(_serviceProvider, compositeExtensions)).Select(x => (IDataConsumer)x.Consumer)]; + ITestSessionLifetimeHandler[] sessionLifetimeHandle = [.. (await testHostManager.BuildTestSessionLifetimeHandleAsync(_serviceProvider, compositeExtensions)).Select(x => (ITestSessionLifetimeHandler)x.TestSessionLifetimeHandler)]; + Assert.HasCount(1, consumers); + Assert.HasCount(1, sessionLifetimeHandle); Assert.AreEqual(compositeExtensions[0].GetInstance(), consumers[0]); Assert.AreEqual(compositeExtensions[0].GetInstance(), sessionLifetimeHandle[0]); } @@ -105,7 +105,7 @@ public async Task TestHostControllerEnvironmentVariableProvider_DuplicatedId_Sho TestHostControllersManager testHostControllerManager = new(); testHostControllerManager.AddEnvironmentVariableProvider(_ => new TestHostEnvironmentVariableProvider("duplicatedId")); testHostControllerManager.AddEnvironmentVariableProvider(_ => new TestHostEnvironmentVariableProvider("duplicatedId")); - InvalidOperationException invalidOperationException = await Assert.ThrowsExceptionAsync(() => testHostControllerManager.BuildAsync(_serviceProvider)); + InvalidOperationException invalidOperationException = await Assert.ThrowsExactlyAsync(() => testHostControllerManager.BuildAsync(_serviceProvider)); Assert.IsTrue(invalidOperationException.Message.Contains("duplicatedId") && invalidOperationException.Message.Contains(typeof(TestHostEnvironmentVariableProvider).ToString())); } @@ -116,7 +116,7 @@ public async Task TestHostControllerEnvironmentVariableProvider_DuplicatedIdWith CompositeExtensionFactory compositeExtensionFactory = new(() => new TestHostEnvironmentVariableProvider("duplicatedId")); testHostControllerManager.AddEnvironmentVariableProvider(_ => new TestHostEnvironmentVariableProvider("duplicatedId")); testHostControllerManager.AddEnvironmentVariableProvider(compositeExtensionFactory); - InvalidOperationException invalidOperationException = await Assert.ThrowsExceptionAsync(() => testHostControllerManager.BuildAsync(_serviceProvider)); + InvalidOperationException invalidOperationException = await Assert.ThrowsExactlyAsync(() => testHostControllerManager.BuildAsync(_serviceProvider)); Assert.IsTrue(invalidOperationException.Message.Contains("duplicatedId") && invalidOperationException.Message.Contains(typeof(TestHostEnvironmentVariableProvider).ToString())); } @@ -126,7 +126,7 @@ public async Task TestHostControllerProcessLifetimeHandler_DuplicatedId_ShouldFa TestHostControllersManager testHostControllerManager = new(); testHostControllerManager.AddProcessLifetimeHandler(_ => new TestHostProcessLifetimeHandler("duplicatedId")); testHostControllerManager.AddProcessLifetimeHandler(_ => new TestHostProcessLifetimeHandler("duplicatedId")); - InvalidOperationException invalidOperationException = await Assert.ThrowsExceptionAsync(() => testHostControllerManager.BuildAsync(_serviceProvider)); + InvalidOperationException invalidOperationException = await Assert.ThrowsExactlyAsync(() => testHostControllerManager.BuildAsync(_serviceProvider)); Assert.IsTrue(invalidOperationException.Message.Contains("duplicatedId") && invalidOperationException.Message.Contains(typeof(TestHostProcessLifetimeHandler).ToString())); } @@ -137,7 +137,7 @@ public async Task TestHostControllerProcessLifetimeHandler_DuplicatedIdWithCompo CompositeExtensionFactory compositeExtensionFactory = new(() => new TestHostProcessLifetimeHandler("duplicatedId")); testHostControllerManager.AddProcessLifetimeHandler(_ => new TestHostProcessLifetimeHandler("duplicatedId")); testHostControllerManager.AddProcessLifetimeHandler(compositeExtensionFactory); - InvalidOperationException invalidOperationException = await Assert.ThrowsExceptionAsync(() => testHostControllerManager.BuildAsync(_serviceProvider)); + InvalidOperationException invalidOperationException = await Assert.ThrowsExactlyAsync(() => testHostControllerManager.BuildAsync(_serviceProvider)); Assert.IsTrue(invalidOperationException.Message.Contains("duplicatedId") && invalidOperationException.Message.Contains(typeof(TestHostProcessLifetimeHandler).ToString())); } @@ -155,8 +155,8 @@ public async Task TestHostController_ComposeFactory_ShouldSucceed(bool withParam testHostControllerManager.AddProcessLifetimeHandler(compositeExtensionFactory); TestHostControllerConfiguration configuration = await testHostControllerManager.BuildAsync(_serviceProvider); Assert.IsTrue(configuration.RequireProcessRestart); - Assert.AreEqual(1, configuration.LifetimeHandlers.Length); - Assert.AreEqual(1, configuration.EnvironmentVariableProviders.Length); + Assert.HasCount(1, configuration.LifetimeHandlers); + Assert.HasCount(1, configuration.EnvironmentVariableProviders); Assert.AreEqual((object)configuration.LifetimeHandlers[0], configuration.EnvironmentVariableProviders[0]); Assert.AreEqual(((ICompositeExtensionFactory)compositeExtensionFactory).GetInstance(), configuration.LifetimeHandlers[0]); Assert.AreEqual(((ICompositeExtensionFactory)compositeExtensionFactory).GetInstance(), configuration.EnvironmentVariableProviders[0]); @@ -171,7 +171,7 @@ public void ComposeFactory_InvalidComposition_ShouldFail(bool withParameter) withParameter ? new CompositeExtensionFactory(sp => new InvalidComposition(sp)) : new CompositeExtensionFactory(() => new InvalidComposition()); - InvalidOperationException invalidOperationException = Assert.ThrowsException(() => ((ICompositeExtensionFactory)compositeExtensionFactory).GetInstance()); + InvalidOperationException invalidOperationException = Assert.ThrowsExactly(() => ((ICompositeExtensionFactory)compositeExtensionFactory).GetInstance()); Assert.AreEqual(CompositeExtensionFactory.ValidateCompositionErrorMessage, invalidOperationException.Message); } @@ -345,7 +345,7 @@ private sealed class Consumer : IDataConsumer public Task ConsumeAsync(IDataProducer dataProducer, IData value, CancellationToken cancellationToken) => throw new NotImplementedException(); } - private sealed class ApplicationLifecycleCallbacks : ITestApplicationLifecycleCallbacks + private sealed class ApplicationLifecycleCallbacks : ITestHostApplicationLifetime { public ApplicationLifecycleCallbacks(string id) => Uid = id; diff --git a/test/UnitTests/TestFramework.UnitTests/Assertions/AssertTests.AreEqualTests.cs b/test/UnitTests/TestFramework.UnitTests/Assertions/AssertTests.AreEqualTests.cs index 3815be857a..a5b4d2223c 100644 --- a/test/UnitTests/TestFramework.UnitTests/Assertions/AssertTests.AreEqualTests.cs +++ b/test/UnitTests/TestFramework.UnitTests/Assertions/AssertTests.AreEqualTests.cs @@ -1,98 +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 AwesomeAssertions; + using TestFramework.ForTestingMSTest; namespace Microsoft.VisualStudio.TestPlatform.TestFramework.UnitTests; public partial class AssertTests : TestContainer { - public void AreNotEqualShouldFailWhenNotEqualType() => - VerifyThrows(() => Assert.AreNotEqual(1, 1)); + public void AreNotEqualShouldFailWhenNotEqualType() + { + Action action = () => Assert.AreNotEqual(1, 1); + action.Should().Throw(); + } public void AreNotEqualShouldFailWhenNotEqualTypeWithMessage() { - Exception ex = VerifyThrows(() => Assert.AreNotEqual(1, 1, "A Message")); - Verify(ex.Message.Contains("A Message")); + Action action = () => Assert.AreNotEqual(1, 1, "A Message"); + action.Should().Throw() + .And.Message.Should().Contain("A Message"); } - public void AreNotEqualShouldFailWhenNotEqualString() => - VerifyThrows(() => Assert.AreNotEqual("A", "A")); + public void AreNotEqualShouldFailWhenNotEqualString() + { + Action action = () => Assert.AreNotEqual("A", "A"); + action.Should().Throw(); + } public void AreNotEqualShouldFailWhenNotEqualStringWithMessage() { - Exception ex = VerifyThrows(() => Assert.AreNotEqual("A", "A", "A Message")); - Verify(ex.Message.Contains("A Message")); + Action action = () => Assert.AreNotEqual("A", "A", "A Message"); + action.Should().Throw() + .And.Message.Should().Contain("A Message"); } [SuppressMessage("Globalization", "CA1304:Specify CultureInfo", Justification = "Testing the API without the culture")] - public void AreNotEqualShouldFailWhenNotEqualStringAndCaseIgnored() => - VerifyThrows(() => Assert.AreNotEqual("A", "a", true)); + public void AreNotEqualShouldFailWhenNotEqualStringAndCaseIgnored() + { + Action action = () => Assert.AreNotEqual("A", "a", true); + action.Should().Throw(); + } - public void AreNotEqualShouldFailWhenNotEqualInt() => - VerifyThrows(() => Assert.AreNotEqual(1, 1)); + public void AreNotEqualShouldFailWhenNotEqualInt() + { + Action action = () => Assert.AreNotEqual(1, 1); + action.Should().Throw(); + } public void AreNotEqualShouldFailWhenNotEqualIntWithMessage() { - Exception ex = VerifyThrows(() => Assert.AreNotEqual(1, 1, "A Message")); - Verify(ex.Message.Contains("A Message")); + Action action = () => Assert.AreNotEqual(1, 1, "A Message"); + action.Should().Throw() + .And.Message.Should().Contain("A Message"); } - public void AreNotEqualShouldFailWhenNotEqualLong() => - VerifyThrows(() => Assert.AreNotEqual(1L, 1L)); + public void AreNotEqualShouldFailWhenNotEqualLong() + { + Action action = () => Assert.AreNotEqual(1L, 1L); + action.Should().Throw(); + } public void AreNotEqualShouldFailWhenNotEqualLongWithMessage() { - Exception ex = VerifyThrows(() => Assert.AreNotEqual(1L, 1L, "A Message")); - Verify(ex.Message.Contains("A Message")); + Action action = () => Assert.AreNotEqual(1L, 1L, "A Message"); + action.Should().Throw() + .And.Message.Should().Contain("A Message"); } - public void AreNotEqualShouldFailWhenNotEqualLongWithDelta() => - VerifyThrows(() => Assert.AreNotEqual(1L, 2L, 1L)); + public void AreNotEqualShouldFailWhenNotEqualLongWithDelta() + { + Action action = () => Assert.AreNotEqual(1L, 2L, 1L); + action.Should().Throw(); + } - public void AreNotEqualShouldFailWhenNotEqualDecimal() => - VerifyThrows(() => Assert.AreNotEqual(0.1M, 0.1M)); + public void AreNotEqualShouldFailWhenNotEqualDecimal() + { + Action action = () => Assert.AreNotEqual(0.1M, 0.1M); + action.Should().Throw(); + } public void AreNotEqualShouldFailWhenNotEqualDecimalWithMessage() { - Exception ex = VerifyThrows(() => Assert.AreNotEqual(0.1M, 0.1M, "A Message")); - Verify(ex.Message.Contains("A Message")); + Action action = () => Assert.AreNotEqual(0.1M, 0.1M, "A Message"); + action.Should().Throw() + .And.Message.Should().Contain("A Message"); } - public void AreNotEqualShouldFailWhenNotEqualDecimalWithDelta() => - VerifyThrows(() => Assert.AreNotEqual(0.1M, 0.2M, 0.1M)); + public void AreNotEqualShouldFailWhenNotEqualDecimalWithDelta() + { + Action action = () => Assert.AreNotEqual(0.1M, 0.2M, 0.1M); + action.Should().Throw(); + } - public void AreNotEqualShouldFailWhenNotEqualDouble() => - VerifyThrows(() => Assert.AreNotEqual(0.1, 0.1)); + public void AreNotEqualShouldFailWhenNotEqualDouble() + { + Action action = () => Assert.AreNotEqual(0.1, 0.1); + action.Should().Throw(); + } public void AreNotEqualShouldFailWhenNotEqualDoubleWithMessage() { - Exception ex = VerifyThrows(() => Assert.AreNotEqual(0.1, 0.1, "A Message")); - Verify(ex.Message.Contains("A Message")); + Action action = () => Assert.AreNotEqual(0.1, 0.1, "A Message"); + action.Should().Throw() + .And.Message.Should().Contain("A Message"); } - public void AreNotEqualShouldFailWhenNotEqualDoubleWithDelta() => - VerifyThrows(() => Assert.AreNotEqual(0.1, 0.2, 0.1)); + public void AreNotEqualShouldFailWhenNotEqualDoubleWithDelta() + { + Action action = () => Assert.AreNotEqual(0.1, 0.2, 0.1); + action.Should().Throw(); + } - public void AreNotEqualShouldFailWhenFloatDouble() => - VerifyThrows(() => Assert.AreNotEqual(100E-2, 100E-2)); + public void AreNotEqualShouldFailWhenFloatDouble() + { + Action action = () => Assert.AreNotEqual(100E-2, 100E-2); + action.Should().Throw(); + } public void AreNotEqualShouldFailWhenFloatDoubleWithMessage() { - Exception ex = VerifyThrows(() => Assert.AreNotEqual(100E-2, 100E-2, "A Message")); - Verify(ex.Message.Contains("A Message")); + Action action = () => Assert.AreNotEqual(100E-2, 100E-2, "A Message"); + action.Should().Throw() + .And.Message.Should().Contain("A Message"); } - public void AreNotEqualShouldFailWhenNotEqualFloatWithDelta() => - VerifyThrows(() => Assert.AreNotEqual(100E-2, 200E-2, 100E-2)); + public void AreNotEqualShouldFailWhenNotEqualFloatWithDelta() + { + Action action = () => Assert.AreNotEqual(100E-2, 200E-2, 100E-2); + action.Should().Throw(); + } - public void AreEqualShouldFailWhenNotEqualType() => - VerifyThrows(() => Assert.AreEqual(null, "string")); + public void AreEqualShouldFailWhenNotEqualType() + { + Action action = () => Assert.AreEqual(null, "string"); + action.Should().Throw(); + } public void AreEqualShouldFailWhenNotEqualTypeWithMessage() { - Exception ex = VerifyThrows(() => Assert.AreEqual(null, "string", "A Message")); - Verify(ex.Message.Contains("A Message")); + Action action = () => Assert.AreEqual(null, "string", "A Message"); + action.Should().Throw() + .And.Message.Should().Contain("A Message"); } public void AreEqual_WithTurkishCultureAndIgnoreCase_Throws() @@ -102,7 +151,8 @@ 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. - VerifyThrows(() => Assert.AreEqual(expected, actual, true, turkishCulture)); + Action action = () => Assert.AreEqual(expected, actual, true, turkishCulture); + action.Should().Throw(); } public void AreEqual_WithEnglishCultureAndIgnoreCase_DoesNotThrow() @@ -122,8 +172,9 @@ 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.Message == "Assert.AreEqual failed. Expected:. Case is different for actual value:. "); + Action action = () => Assert.AreEqual(expected, actual, false, englishCulture); + action.Should().Throw() + .And.Message.Should().Be("Assert.AreEqual failed. Expected:. Case is different for actual value:. "); } public void AreEqual_WithTurkishCultureAndDoesNotIgnoreCase_Throws() @@ -133,86 +184,125 @@ public void AreEqual_WithTurkishCultureAndDoesNotIgnoreCase_Throws() var turkishCulture = new CultureInfo("tr-TR"); // Won't ignore case. - VerifyThrows(() => Assert.AreEqual(expected, actual, false, turkishCulture)); + Action action = () => Assert.AreEqual(expected, actual, false, turkishCulture); + action.Should().Throw(); } public void AreEqualShouldFailWhenNotEqualStringWithMessage() { - Exception ex = VerifyThrows(() => Assert.AreEqual("A", "a", "A Message")); - Verify(ex.Message.Contains("A Message")); + Action action = () => Assert.AreEqual("A", "a", "A Message"); + action.Should().Throw() + .And.Message.Should().Contain("A Message"); } [SuppressMessage("Globalization", "CA1304:Specify CultureInfo", Justification = "Testing the API without the culture")] - public void AreEqualShouldFailWhenNotEqualStringAndCaseIgnored() => - VerifyThrows(() => Assert.AreEqual("A", "a", false)); + public void AreEqualShouldFailWhenNotEqualStringAndCaseIgnored() + { + Action action = () => Assert.AreEqual("A", "a", false); + action.Should().Throw(); + } - public void AreEqualShouldFailWhenNotEqualInt() => - VerifyThrows(() => Assert.AreEqual(1, 2)); + public void AreEqualShouldFailWhenNotEqualInt() + { + Action action = () => Assert.AreEqual(1, 2); + action.Should().Throw(); + } public void AreEqualShouldFailWhenNotEqualIntWithMessage() { - Exception ex = VerifyThrows(() => Assert.AreEqual(1, 2, "A Message")); - Verify(ex.Message.Contains("A Message")); + Action action = () => Assert.AreEqual(1, 2, "A Message"); + action.Should().Throw() + .And.Message.Should().Contain("A Message"); } - public void AreEqualShouldFailWhenNotEqualLong() => - VerifyThrows(() => Assert.AreEqual(1L, 2L)); + public void AreEqualShouldFailWhenNotEqualLong() + { + Action action = () => Assert.AreEqual(1L, 2L); + action.Should().Throw(); + } public void AreEqualShouldFailWhenNotEqualLongWithMessage() { - Exception ex = VerifyThrows(() => Assert.AreEqual(1L, 2L, "A Message")); - Verify(ex.Message.Contains("A Message")); + Action action = () => Assert.AreEqual(1L, 2L, "A Message"); + action.Should().Throw() + .And.Message.Should().Contain("A Message"); } - public void AreEqualShouldFailWhenNotEqualLongWithDelta() => - VerifyThrows(() => Assert.AreEqual(10L, 20L, 5L)); + public void AreEqualShouldFailWhenNotEqualLongWithDelta() + { + Action action = () => Assert.AreEqual(10L, 20L, 5L); + action.Should().Throw(); + } - public void AreEqualShouldFailWhenNotEqualDouble() => - VerifyThrows(() => Assert.AreEqual(0.1, 0.2)); + public void AreEqualShouldFailWhenNotEqualDouble() + { + Action action = () => Assert.AreEqual(0.1, 0.2); + action.Should().Throw(); + } public void AreEqualShouldFailWhenNotEqualDoubleWithMessage() { - Exception ex = VerifyThrows(() => Assert.AreEqual(0.1, 0.2, "A Message")); - Verify(ex.Message.Contains("A Message")); + Action action = () => Assert.AreEqual(0.1, 0.2, "A Message"); + action.Should().Throw() + .And.Message.Should().Contain("A Message"); } - public void AreEqualShouldFailWhenNotEqualDoubleWithDelta() => - VerifyThrows(() => Assert.AreEqual(0.1, 0.2, 0.05)); + public void AreEqualShouldFailWhenNotEqualDoubleWithDelta() + { + Action action = () => Assert.AreEqual(0.1, 0.2, 0.05); + action.Should().Throw(); + } public void AreEqualShouldFailWhenNotEqualDecimal() { static void Action() => Assert.AreEqual(0.1M, 0.2M); - VerifyThrows(Action); + Action action = Action; + action.Should().Throw(); } public void AreEqualShouldFailWhenNotEqualDecimalWithMessage() { - Exception ex = VerifyThrows(() => Assert.AreEqual(0.1M, 0.2M, "A Message")); - Verify(ex.Message.Contains("A Message")); + Action action = () => Assert.AreEqual(0.1M, 0.2M, "A Message"); + action.Should().Throw() + .And.Message.Should().Contain("A Message"); } - public void AreEqualShouldFailWhenNotEqualDecimalWithDelta() => - VerifyThrows(() => Assert.AreEqual(0.1M, 0.2M, 0.05M)); + public void AreEqualShouldFailWhenNotEqualDecimalWithDelta() + { + Action action = () => Assert.AreEqual(0.1M, 0.2M, 0.05M); + action.Should().Throw(); + } - public void AreEqualShouldFailWhenFloatDouble() => - VerifyThrows(() => Assert.AreEqual(100E-2, 200E-2)); + public void AreEqualShouldFailWhenFloatDouble() + { + Action action = () => Assert.AreEqual(100E-2, 200E-2); + action.Should().Throw(); + } public void AreEqualShouldFailWhenFloatDoubleWithMessage() { - Exception ex = VerifyThrows(() => Assert.AreEqual(100E-2, 200E-2, "A Message")); - Verify(ex.Message.Contains("A Message")); + Action action = () => Assert.AreEqual(100E-2, 200E-2, "A Message"); + action.Should().Throw() + .And.Message.Should().Contain("A Message"); } - public void AreEqualShouldFailWhenNotEqualFloatWithDelta() => - VerifyThrows(() => Assert.AreEqual(100E-2, 200E-2, 50E-2)); + public void AreEqualShouldFailWhenNotEqualFloatWithDelta() + { + Action action = () => Assert.AreEqual(100E-2, 200E-2, 50E-2); + action.Should().Throw(); + } - public void AreEqualTwoObjectsShouldFail() => - VerifyThrows(() => Assert.AreEqual(new object(), new object())); + public void AreEqualTwoObjectsShouldFail() + { + Action action = () => Assert.AreEqual(new object(), new object()); + action.Should().Throw(); + } public void AreEqualTwoObjectsDifferentTypeShouldFail() { - AssertFailedException ex = VerifyThrows(() => Assert.AreEqual(new object(), 1)); - Verify(ex.Message.Contains("Assert.AreEqual failed. Expected:. Actual:<1 (System.Int32)>.")); + Action action = () => Assert.AreEqual(new object(), 1); + action.Should().Throw() + .And.Message.Should().Contain("Assert.AreEqual failed. Expected:. Actual:<1 (System.Int32)>."); } public void AreEqualWithTypeOverridingEqualsShouldWork() @@ -238,7 +328,8 @@ static void Action() Assert.AreEqual(a, b, new TypeOverridesEqualsEqualityComparer()); } - VerifyThrows(Action); + Action action = Action; + action.Should().Throw(); } public void AreEqualUsingCustomIEquatable() @@ -250,7 +341,8 @@ public void AreEqualUsingCustomIEquatable() Assert.AreEqual(instanceOfA, instanceOfB); // This one doesn't work - VerifyThrows(() => Assert.AreEqual(instanceOfB, instanceOfA)); + Action action = () => Assert.AreEqual(instanceOfB, instanceOfA); + action.Should().Throw(); } #pragma warning disable IDE0004 @@ -270,307 +362,347 @@ public void GenericAreEqual_InterpolatedString_EqualValues_ShouldPass() { DummyClassTrackingToStringCalls o = new(); Assert.AreEqual(o, o, $"User-provided message: {o}"); - Verify(!o.WasToStringCalled); + o.WasToStringCalled.Should().BeFalse(); } public async Task GenericAreEqual_InterpolatedString_DifferentValues_ShouldFail() { DummyClassTrackingToStringCalls o = new(); DateTime dateTime = DateTime.Now; - Exception ex = await VerifyThrowsAsync(async () => Assert.AreEqual(0, 1, $"User-provided message. {o}, {o,35}, {await GetHelloStringAsync()}, {new DummyIFormattable()}, {dateTime:tt}, {dateTime,5:tt}")); - Verify(ex.Message == $"Assert.AreEqual failed. Expected:<0>. Actual:<1>. User-provided message. DummyClassTrackingToStringCalls, DummyClassTrackingToStringCalls, Hello, DummyIFormattable.ToString(), {string.Format(null, "{0:tt}", dateTime)}, {string.Format(null, "{0,5:tt}", dateTime)}"); - Verify(o.WasToStringCalled); + Func action = async () => Assert.AreEqual(0, 1, $"User-provided message. {o}, {o,35}, {await GetHelloStringAsync()}, {new DummyIFormattable()}, {dateTime:tt}, {dateTime,5:tt}"); + (await action.Should().ThrowAsync()) + .And.Message.Should().Be($"Assert.AreEqual failed. Expected:<0>. Actual:<1>. User-provided message. DummyClassTrackingToStringCalls, DummyClassTrackingToStringCalls, Hello, DummyIFormattable.ToString(), {string.Format(null, "{0:tt}", dateTime)}, {string.Format(null, "{0,5:tt}", dateTime)}"); + o.WasToStringCalled.Should().BeTrue(); } public void GenericAreNotEqual_InterpolatedString_DifferentValues_ShouldPass() { DummyClassTrackingToStringCalls o = new(); Assert.AreNotEqual(0, 1, $"User-provided message: {o}"); - Verify(!o.WasToStringCalled); + o.WasToStringCalled.Should().BeFalse(); } public async Task GenericAreNotEqual_InterpolatedString_SameValues_ShouldFail() { DummyClassTrackingToStringCalls o = new(); DateTime dateTime = DateTime.Now; - Exception ex = await VerifyThrowsAsync(async () => Assert.AreNotEqual(0, 0, $"User-provided message. {o}, {o,35}, {await GetHelloStringAsync()}, {new DummyIFormattable()}, {dateTime:tt}, {dateTime,5:tt}")); - Verify(ex.Message == $"Assert.AreNotEqual failed. Expected any value except:<0>. Actual:<0>. User-provided message. DummyClassTrackingToStringCalls, DummyClassTrackingToStringCalls, Hello, DummyIFormattable.ToString(), {string.Format(null, "{0:tt}", dateTime)}, {string.Format(null, "{0,5:tt}", dateTime)}"); - Verify(o.WasToStringCalled); + Func action = async () => Assert.AreNotEqual(0, 0, $"User-provided message. {o}, {o,35}, {await GetHelloStringAsync()}, {new DummyIFormattable()}, {dateTime:tt}, {dateTime,5:tt}"); + (await action.Should().ThrowAsync()) + .And.Message.Should().Be($"Assert.AreNotEqual failed. Expected any value except:<0>. Actual:<0>. User-provided message. DummyClassTrackingToStringCalls, DummyClassTrackingToStringCalls, Hello, DummyIFormattable.ToString(), {string.Format(null, "{0:tt}", dateTime)}, {string.Format(null, "{0,5:tt}", dateTime)}"); + o.WasToStringCalled.Should().BeTrue(); } public void FloatAreEqual_InterpolatedString_EqualValues_ShouldPass() { DummyClassTrackingToStringCalls o = new(); Assert.AreEqual(1.0f, 1.1f, delta: 0.2f, $"User-provided message: {o}"); - Verify(!o.WasToStringCalled); + o.WasToStringCalled.Should().BeFalse(); } public async Task FloatAreEqual_InterpolatedString_DifferentValues_ShouldFail() { DummyClassTrackingToStringCalls o = new(); DateTime dateTime = DateTime.Now; - Exception ex = await VerifyThrowsAsync(async () => Assert.AreEqual(1.0f, 1.1f, 0.001f, $"User-provided message. {o}, {o,35}, {await GetHelloStringAsync()}, {new DummyIFormattable()}, {dateTime:tt}, {dateTime,5:tt}")); - Verify(ex.Message == $"Assert.AreEqual failed. Expected a difference no greater than <0.001> between expected value <1> and actual value <1.1>. User-provided message. DummyClassTrackingToStringCalls, DummyClassTrackingToStringCalls, Hello, DummyIFormattable.ToString(), {string.Format(null, "{0:tt}", dateTime)}, {string.Format(null, "{0,5:tt}", dateTime)}"); - Verify(o.WasToStringCalled); + Func action = async () => Assert.AreEqual(1.0f, 1.1f, 0.001f, $"User-provided message. {o}, {o,35}, {await GetHelloStringAsync()}, {new DummyIFormattable()}, {dateTime:tt}, {dateTime,5:tt}"); + (await action.Should().ThrowAsync()) + .And.Message.Should().Be($"Assert.AreEqual failed. Expected a difference no greater than <0.001> between expected value <1> and actual value <1.1>. User-provided message. DummyClassTrackingToStringCalls, DummyClassTrackingToStringCalls, Hello, DummyIFormattable.ToString(), {string.Format(null, "{0:tt}", dateTime)}, {string.Format(null, "{0,5:tt}", dateTime)}"); + o.WasToStringCalled.Should().BeTrue(); } public void FloatAreNotEqual_InterpolatedString_DifferentValues_ShouldPass() { DummyClassTrackingToStringCalls o = new(); Assert.AreNotEqual(1.0f, 1.1f, 0.001f, $"User-provided message: {o}"); - Verify(!o.WasToStringCalled); + o.WasToStringCalled.Should().BeFalse(); } public async Task FloatAreNotEqual_InterpolatedString_SameValues_ShouldFail() { DummyClassTrackingToStringCalls o = new(); DateTime dateTime = DateTime.Now; - Exception ex = await VerifyThrowsAsync(async () => Assert.AreNotEqual(1.0f, 1.1f, 0.2f, $"User-provided message. {o}, {o,35}, {await GetHelloStringAsync()}, {new DummyIFormattable()}, {dateTime:tt}, {dateTime,5:tt}")); - Verify(ex.Message == $"Assert.AreNotEqual failed. Expected a difference greater than <0.2> between expected value <1> and actual value <1.1>. User-provided message. DummyClassTrackingToStringCalls, DummyClassTrackingToStringCalls, Hello, DummyIFormattable.ToString(), {string.Format(null, "{0:tt}", dateTime)}, {string.Format(null, "{0,5:tt}", dateTime)}"); - Verify(o.WasToStringCalled); + Func action = async () => Assert.AreNotEqual(1.0f, 1.1f, 0.2f, $"User-provided message. {o}, {o,35}, {await GetHelloStringAsync()}, {new DummyIFormattable()}, {dateTime:tt}, {dateTime,5:tt}"); + (await action.Should().ThrowAsync()) + .And.Message.Should().Be($"Assert.AreNotEqual failed. Expected a difference greater than <0.2> between expected value <1> and actual value <1.1>. User-provided message. DummyClassTrackingToStringCalls, DummyClassTrackingToStringCalls, Hello, DummyIFormattable.ToString(), {string.Format(null, "{0:tt}", dateTime)}, {string.Format(null, "{0,5:tt}", dateTime)}"); + o.WasToStringCalled.Should().BeTrue(); } public void DecimalAreEqual_InterpolatedString_EqualValues_ShouldPass() { DummyClassTrackingToStringCalls o = new(); Assert.AreEqual(1.0m, 1.1m, delta: 0.2m, $"User-provided message: {o}"); - Verify(!o.WasToStringCalled); + o.WasToStringCalled.Should().BeFalse(); } public async Task DecimalAreEqual_InterpolatedString_DifferentValues_ShouldFail() { DummyClassTrackingToStringCalls o = new(); DateTime dateTime = DateTime.Now; - Exception ex = await VerifyThrowsAsync(async () => Assert.AreEqual(1.0m, 1.1m, 0.001m, $"User-provided message. {o}, {o,35}, {await GetHelloStringAsync()}, {new DummyIFormattable()}, {dateTime:tt}, {dateTime,5:tt}")); - Verify(ex.Message == $"Assert.AreEqual failed. Expected a difference no greater than <0.001> between expected value <1.0> and actual value <1.1>. User-provided message. DummyClassTrackingToStringCalls, DummyClassTrackingToStringCalls, Hello, DummyIFormattable.ToString(), {string.Format(null, "{0:tt}", dateTime)}, {string.Format(null, "{0,5:tt}", dateTime)}"); - Verify(o.WasToStringCalled); + Func action = async () => Assert.AreEqual(1.0m, 1.1m, 0.001m, $"User-provided message. {o}, {o,35}, {await GetHelloStringAsync()}, {new DummyIFormattable()}, {dateTime:tt}, {dateTime,5:tt}"); + (await action.Should().ThrowAsync()) + .And.Message.Should().Be($"Assert.AreEqual failed. Expected a difference no greater than <0.001> between expected value <1.0> and actual value <1.1>. User-provided message. DummyClassTrackingToStringCalls, DummyClassTrackingToStringCalls, Hello, DummyIFormattable.ToString(), {string.Format(null, "{0:tt}", dateTime)}, {string.Format(null, "{0,5:tt}", dateTime)}"); + o.WasToStringCalled.Should().BeTrue(); } public void DecimalAreNotEqual_InterpolatedString_DifferentValues_ShouldPass() { DummyClassTrackingToStringCalls o = new(); Assert.AreNotEqual(1.0m, 1.1m, 0.001m, $"User-provided message: {o}"); - Verify(!o.WasToStringCalled); + o.WasToStringCalled.Should().BeFalse(); } public async Task DecimalAreNotEqual_InterpolatedString_SameValues_ShouldFail() { DummyClassTrackingToStringCalls o = new(); DateTime dateTime = DateTime.Now; - Exception ex = await VerifyThrowsAsync(async () => Assert.AreNotEqual(1.0m, 1.1m, 0.2m, $"User-provided message. {o}, {o,35}, {await GetHelloStringAsync()}, {new DummyIFormattable()}, {dateTime:tt}, {dateTime,5:tt}")); - Verify(ex.Message == $"Assert.AreNotEqual failed. Expected a difference greater than <0.2> between expected value <1.0> and actual value <1.1>. User-provided message. DummyClassTrackingToStringCalls, DummyClassTrackingToStringCalls, Hello, DummyIFormattable.ToString(), {string.Format(null, "{0:tt}", dateTime)}, {string.Format(null, "{0,5:tt}", dateTime)}"); - Verify(o.WasToStringCalled); + Func action = async () => Assert.AreNotEqual(1.0m, 1.1m, 0.2m, $"User-provided message. {o}, {o,35}, {await GetHelloStringAsync()}, {new DummyIFormattable()}, {dateTime:tt}, {dateTime,5:tt}"); + (await action.Should().ThrowAsync()) + .And.Message.Should().Be($"Assert.AreNotEqual failed. Expected a difference greater than <0.2> between expected value <1.0> and actual value <1.1>. User-provided message. DummyClassTrackingToStringCalls, DummyClassTrackingToStringCalls, Hello, DummyIFormattable.ToString(), {string.Format(null, "{0:tt}", dateTime)}, {string.Format(null, "{0,5:tt}", dateTime)}"); + o.WasToStringCalled.Should().BeTrue(); } public void LongAreEqual_InterpolatedString_EqualValues_ShouldPass() { DummyClassTrackingToStringCalls o = new(); Assert.AreEqual(1L, 2L, delta: 1L, $"User-provided message: {o}"); - Verify(!o.WasToStringCalled); + o.WasToStringCalled.Should().BeFalse(); } public async Task LongAreEqual_InterpolatedString_DifferentValues_ShouldFail() { DummyClassTrackingToStringCalls o = new(); DateTime dateTime = DateTime.Now; - Exception ex = await VerifyThrowsAsync(async () => Assert.AreEqual(1L, 2L, 0L, $"User-provided message. {o}, {o,35}, {await GetHelloStringAsync()}, {new DummyIFormattable()}, {dateTime:tt}, {dateTime,5:tt}")); - Verify(ex.Message == $"Assert.AreEqual failed. Expected a difference no greater than <0> between expected value <1> and actual value <2>. User-provided message. DummyClassTrackingToStringCalls, DummyClassTrackingToStringCalls, Hello, DummyIFormattable.ToString(), {string.Format(null, "{0:tt}", dateTime)}, {string.Format(null, "{0,5:tt}", dateTime)}"); - Verify(o.WasToStringCalled); + Func action = async () => Assert.AreEqual(1L, 2L, 0L, $"User-provided message. {o}, {o,35}, {await GetHelloStringAsync()}, {new DummyIFormattable()}, {dateTime:tt}, {dateTime,5:tt}"); + (await action.Should().ThrowAsync()) + .And.Message.Should().Be($"Assert.AreEqual failed. Expected a difference no greater than <0> between expected value <1> and actual value <2>. User-provided message. DummyClassTrackingToStringCalls, DummyClassTrackingToStringCalls, Hello, DummyIFormattable.ToString(), {string.Format(null, "{0:tt}", dateTime)}, {string.Format(null, "{0,5:tt}", dateTime)}"); + o.WasToStringCalled.Should().BeTrue(); } public void LongAreNotEqual_InterpolatedString_DifferentValues_ShouldPass() { DummyClassTrackingToStringCalls o = new(); Assert.AreNotEqual(1L, 2L, 0L, $"User-provided message: {o}"); - Verify(!o.WasToStringCalled); + o.WasToStringCalled.Should().BeFalse(); } public async Task LongAreNotEqual_InterpolatedString_SameValues_ShouldFail() { DummyClassTrackingToStringCalls o = new(); DateTime dateTime = DateTime.Now; - Exception ex = await VerifyThrowsAsync(async () => Assert.AreNotEqual(1L, 2L, 1L, $"User-provided message. {o}, {o,35}, {await GetHelloStringAsync()}, {new DummyIFormattable()}, {dateTime:tt}, {dateTime,5:tt}")); - Verify(ex.Message == $"Assert.AreNotEqual failed. Expected a difference greater than <1> between expected value <1> and actual value <2>. User-provided message. DummyClassTrackingToStringCalls, DummyClassTrackingToStringCalls, Hello, DummyIFormattable.ToString(), {string.Format(null, "{0:tt}", dateTime)}, {string.Format(null, "{0,5:tt}", dateTime)}"); - Verify(o.WasToStringCalled); + Func action = async () => Assert.AreNotEqual(1L, 2L, 1L, $"User-provided message. {o}, {o,35}, {await GetHelloStringAsync()}, {new DummyIFormattable()}, {dateTime:tt}, {dateTime,5:tt}"); + (await action.Should().ThrowAsync()) + .And.Message.Should().Be($"Assert.AreNotEqual failed. Expected a difference greater than <1> between expected value <1> and actual value <2>. User-provided message. DummyClassTrackingToStringCalls, DummyClassTrackingToStringCalls, Hello, DummyIFormattable.ToString(), {string.Format(null, "{0:tt}", dateTime)}, {string.Format(null, "{0,5:tt}", dateTime)}"); + o.WasToStringCalled.Should().BeTrue(); } public void DoubleAreEqual_InterpolatedString_EqualValues_ShouldPass() { DummyClassTrackingToStringCalls o = new(); Assert.AreEqual(1.0d, 1.1d, delta: 0.2d, $"User-provided message: {o}"); - Verify(!o.WasToStringCalled); + o.WasToStringCalled.Should().BeFalse(); } public async Task DoubleAreEqual_InterpolatedString_DifferentValues_ShouldFail() { DummyClassTrackingToStringCalls o = new(); DateTime dateTime = DateTime.Now; - Exception ex = await VerifyThrowsAsync(async () => Assert.AreEqual(1.0d, 1.1d, 0.001d, $"User-provided message. {o}, {o,35}, {await GetHelloStringAsync()}, {new DummyIFormattable()}, {dateTime:tt}, {dateTime,5:tt}")); - Verify(ex.Message == $"Assert.AreEqual failed. Expected a difference no greater than <0.001> between expected value <1> and actual value <1.1>. User-provided message. DummyClassTrackingToStringCalls, DummyClassTrackingToStringCalls, Hello, DummyIFormattable.ToString(), {string.Format(null, "{0:tt}", dateTime)}, {string.Format(null, "{0,5:tt}", dateTime)}"); - Verify(o.WasToStringCalled); + Func action = async () => Assert.AreEqual(1.0d, 1.1d, 0.001d, $"User-provided message. {o}, {o,35}, {await GetHelloStringAsync()}, {new DummyIFormattable()}, {dateTime:tt}, {dateTime,5:tt}"); + (await action.Should().ThrowAsync()) + .And.Message.Should().Be($"Assert.AreEqual failed. Expected a difference no greater than <0.001> between expected value <1> and actual value <1.1>. User-provided message. DummyClassTrackingToStringCalls, DummyClassTrackingToStringCalls, Hello, DummyIFormattable.ToString(), {string.Format(null, "{0:tt}", dateTime)}, {string.Format(null, "{0,5:tt}", dateTime)}"); + o.WasToStringCalled.Should().BeTrue(); } public void DoubleAreNotEqual_InterpolatedString_DifferentValues_ShouldPass() { DummyClassTrackingToStringCalls o = new(); Assert.AreNotEqual(1.0d, 1.1d, 0.001d, $"User-provided message: {o}"); - Verify(!o.WasToStringCalled); + o.WasToStringCalled.Should().BeFalse(); } public async Task DoubleAreNotEqual_InterpolatedString_SameValues_ShouldFail() { DummyClassTrackingToStringCalls o = new(); DateTime dateTime = DateTime.Now; - Exception ex = await VerifyThrowsAsync(async () => Assert.AreNotEqual(1.0d, 1.1d, 0.2d, $"User-provided message. {o}, {o,35}, {await GetHelloStringAsync()}, {new DummyIFormattable()}, {dateTime:tt}, {dateTime,5:tt}")); - Verify(ex.Message == $"Assert.AreNotEqual failed. Expected a difference greater than <0.2> between expected value <1> and actual value <1.1>. User-provided message. DummyClassTrackingToStringCalls, DummyClassTrackingToStringCalls, Hello, DummyIFormattable.ToString(), {string.Format(null, "{0:tt}", dateTime)}, {string.Format(null, "{0,5:tt}", dateTime)}"); - Verify(o.WasToStringCalled); + Func action = async () => Assert.AreNotEqual(1.0d, 1.1d, 0.2d, $"User-provided message. {o}, {o,35}, {await GetHelloStringAsync()}, {new DummyIFormattable()}, {dateTime:tt}, {dateTime,5:tt}"); + (await action.Should().ThrowAsync()) + .And.Message.Should().Be($"Assert.AreNotEqual failed. Expected a difference greater than <0.2> between expected value <1> and actual value <1.1>. User-provided message. DummyClassTrackingToStringCalls, DummyClassTrackingToStringCalls, Hello, DummyIFormattable.ToString(), {string.Format(null, "{0:tt}", dateTime)}, {string.Format(null, "{0,5:tt}", dateTime)}"); + o.WasToStringCalled.Should().BeTrue(); } public void FloatAreEqual_ExpectedIsNumeric_ActualIsNumeric_ExpectedAndActualNotEquals_DeltaIsNaN_ShouldFail() { - Exception ex = VerifyThrows(() => Assert.AreEqual(1.0f, 2.0f, float.NaN)); - Verify(ex.Message is """ + Action action = () => Assert.AreEqual(1.0f, 2.0f, float.NaN); + action.Should().Throw().And.Message.Should().BeOneOf( + """ Specified argument was out of the range of valid values. Parameter name: delta - """ or "Specified argument was out of the range of valid values. (Parameter 'delta')"); + """, + "Specified argument was out of the range of valid values. (Parameter 'delta')"); } public void FloatAreEqual_ExpectedIsNumeric_ActualIsNumeric_ExpectedAndActualEquals_DeltaIsNaN_ShouldFail() { - Exception ex = VerifyThrows(() => Assert.AreEqual(1.0f, 1.0f, float.NaN)); - Verify(ex.Message is """ + Action action = () => Assert.AreEqual(1.0f, 1.0f, float.NaN); + action.Should().Throw().And.Message.Should().BeOneOf( + """ Specified argument was out of the range of valid values. Parameter name: delta - """ or "Specified argument was out of the range of valid values. (Parameter 'delta')"); + """, + "Specified argument was out of the range of valid values. (Parameter 'delta')"); } public void FloatAreEqual_ExpectedIsNaN_ActualIsNumeric_DeltaIsNaN_ShouldFail() { - Exception ex = VerifyThrows(() => Assert.AreEqual(float.NaN, 1.0f, float.NaN)); - Verify(ex.Message is """ + Action action = () => Assert.AreEqual(float.NaN, 1.0f, float.NaN); + action.Should().Throw().And.Message.Should().BeOneOf( + """ Specified argument was out of the range of valid values. Parameter name: delta - """ or "Specified argument was out of the range of valid values. (Parameter 'delta')"); + """, + "Specified argument was out of the range of valid values. (Parameter 'delta')"); } public void FloatAreEqual_ExpectedIsNaN_ActualIsNaN_DeltaIsNaN_ShouldFail() { - Exception ex = VerifyThrows(() => Assert.AreEqual(float.NaN, float.NaN, float.NaN)); - Verify(ex.Message is """ + Action action = () => Assert.AreEqual(float.NaN, float.NaN, float.NaN); + action.Should().Throw().And.Message.Should().BeOneOf( + """ Specified argument was out of the range of valid values. Parameter name: delta - """ or "Specified argument was out of the range of valid values. (Parameter 'delta')"); + """, + "Specified argument was out of the range of valid values. (Parameter 'delta')"); } public void FloatAreEqual_ExpectedIsNumeric_ActualIsNaN_DeltaIsNaN_ShouldFail() { - Exception ex = VerifyThrows(() => Assert.AreEqual(1.0f, float.NaN, float.NaN)); - Verify(ex.Message is """ + Action action = () => Assert.AreEqual(1.0f, float.NaN, float.NaN); + action.Should().Throw().And.Message.Should().BeOneOf( + """ Specified argument was out of the range of valid values. Parameter name: delta - """ or "Specified argument was out of the range of valid values. (Parameter 'delta')"); + """, + "Specified argument was out of the range of valid values. (Parameter 'delta')"); } public void FloatAreEqual_ExpectedIsNumeric_ActualIsNumeric_ExpectedAndActualNotEquals_DeltaIsNegative_ShouldFail() { - Exception ex = VerifyThrows(() => Assert.AreEqual(1.0f, 2.0f, -1.0f)); - Verify(ex.Message is """ + Action action = () => Assert.AreEqual(1.0f, 2.0f, -1.0f); + action.Should().Throw().And.Message.Should().BeOneOf( + """ Specified argument was out of the range of valid values. Parameter name: delta - """ or "Specified argument was out of the range of valid values. (Parameter 'delta')"); + """, + "Specified argument was out of the range of valid values. (Parameter 'delta')"); } public void FloatAreEqual_ExpectedIsNumeric_ActualIsNumeric_ExpectedAndActualEquals_DeltaIsNegative_ShouldFail() { - Exception ex = VerifyThrows(() => Assert.AreEqual(1.0f, 1.0f, -1.0f)); - Verify(ex.Message is """ + Action action = () => Assert.AreEqual(1.0f, 1.0f, -1.0f); + action.Should().Throw().And.Message.Should().BeOneOf( + """ Specified argument was out of the range of valid values. Parameter name: delta - """ or "Specified argument was out of the range of valid values. (Parameter 'delta')"); + """, + "Specified argument was out of the range of valid values. (Parameter 'delta')"); } public void FloatAreEqual_ExpectedIsNaN_ActualIsNumeric_DeltaIsNegative_ShouldFail() { - Exception ex = VerifyThrows(() => Assert.AreEqual(float.NaN, 1.0f, -1.0f)); - Verify(ex.Message is """ + Action action = () => Assert.AreEqual(float.NaN, 1.0f, -1.0f); + action.Should().Throw().And.Message.Should().BeOneOf( + """ Specified argument was out of the range of valid values. Parameter name: delta - """ or "Specified argument was out of the range of valid values. (Parameter 'delta')"); + """, + "Specified argument was out of the range of valid values. (Parameter 'delta')"); } public void FloatAreEqual_ExpectedIsNaN_ActualIsNaN_DeltaIsNegative_ShouldFail() { - Exception ex = VerifyThrows(() => Assert.AreEqual(float.NaN, float.NaN, -1.0f)); - Verify(ex.Message is """ + Action action = () => Assert.AreEqual(float.NaN, float.NaN, -1.0f); + action.Should().Throw().And.Message.Should().BeOneOf( + """ Specified argument was out of the range of valid values. Parameter name: delta - """ or "Specified argument was out of the range of valid values. (Parameter 'delta')"); + """, + "Specified argument was out of the range of valid values. (Parameter 'delta')"); } public void FloatAreEqual_ExpectedIsNumeric_ActualIsNaN_DeltaIsNegative_ShouldFail() { - Exception ex = VerifyThrows(() => Assert.AreEqual(1.0f, float.NaN, -1.0f)); - Verify(ex.Message is """ + Action action = () => Assert.AreEqual(1.0f, float.NaN, -1.0f); + action.Should().Throw().And.Message.Should().BeOneOf( + """ Specified argument was out of the range of valid values. Parameter name: delta - """ or "Specified argument was out of the range of valid values. (Parameter 'delta')"); + """, + "Specified argument was out of the range of valid values. (Parameter 'delta')"); } public void FloatAreEqual_ExpectedIsNumeric_ActualIsNumeric_ExpectedAndActualNotEquals_DeltaIsNegativeInf_ShouldFail() { - Exception ex = VerifyThrows(() => Assert.AreEqual(1.0f, 2.0f, float.NegativeInfinity)); - Verify(ex.Message is """ + Action action = () => Assert.AreEqual(1.0f, 2.0f, float.NegativeInfinity); + action.Should().Throw().And.Message.Should().BeOneOf( + """ Specified argument was out of the range of valid values. Parameter name: delta - """ or "Specified argument was out of the range of valid values. (Parameter 'delta')"); + """, + "Specified argument was out of the range of valid values. (Parameter 'delta')"); } public void FloatAreEqual_ExpectedIsNumeric_ActualIsNumeric_ExpectedAndActualEquals_DeltaIsNegativeInf_ShouldFail() { - Exception ex = VerifyThrows(() => Assert.AreEqual(1.0f, 1.0f, float.NegativeInfinity)); - Verify(ex.Message is """ + Action action = () => Assert.AreEqual(1.0f, 1.0f, float.NegativeInfinity); + action.Should().Throw().And.Message.Should().BeOneOf( + """ Specified argument was out of the range of valid values. Parameter name: delta - """ or "Specified argument was out of the range of valid values. (Parameter 'delta')"); + """, + "Specified argument was out of the range of valid values. (Parameter 'delta')"); } public void FloatAreEqual_ExpectedIsNaN_ActualIsNumeric_DeltaIsNegativeInf_ShouldFail() { - Exception ex = VerifyThrows(() => Assert.AreEqual(float.NaN, 1.0f, float.NegativeInfinity)); - Verify(ex.Message is """ + Action action = () => Assert.AreEqual(float.NaN, 1.0f, float.NegativeInfinity); + action.Should().Throw().And.Message.Should().BeOneOf( + """ Specified argument was out of the range of valid values. Parameter name: delta - """ or "Specified argument was out of the range of valid values. (Parameter 'delta')"); + """, + "Specified argument was out of the range of valid values. (Parameter 'delta')"); } public void FloatAreEqual_ExpectedIsNaN_ActualIsNaN_DeltaIsNegativeInf_ShouldFail() { - Exception ex = VerifyThrows(() => Assert.AreEqual(float.NaN, float.NaN, -1.0f)); - Verify(ex.Message is """ + Action action = () => Assert.AreEqual(float.NaN, float.NaN, -1.0f); + action.Should().Throw().And.Message.Should().BeOneOf( + """ Specified argument was out of the range of valid values. Parameter name: delta - """ or "Specified argument was out of the range of valid values. (Parameter 'delta')"); + """, + "Specified argument was out of the range of valid values. (Parameter 'delta')"); } public void FloatAreEqual_ExpectedIsNumeric_ActualIsNaN_DeltaIsNegativeInf_ShouldFail() { - Exception ex = VerifyThrows(() => Assert.AreEqual(1.0f, float.NaN, float.NegativeInfinity)); - Verify(ex.Message is """ + Action action = () => Assert.AreEqual(1.0f, float.NaN, float.NegativeInfinity); + action.Should().Throw().And.Message.Should().BeOneOf( + """ Specified argument was out of the range of valid values. Parameter name: delta - """ or "Specified argument was out of the range of valid values. (Parameter 'delta')"); + """, + "Specified argument was out of the range of valid values. (Parameter 'delta')"); } public void FloatAreEqual_ExpectedIsNumeric_ActualIsNumeric_ExpectedAndActualDifferenceGreaterThanDeltaPositive_DeltaIsNumeric_ShouldFail() { - Exception ex = VerifyThrows(() => Assert.AreEqual(5.0f, 2.0f, 2.0f)); // difference is 3. Delta is 2 - Verify(ex.Message == "Assert.AreEqual failed. Expected a difference no greater than <2> between expected value <5> and actual value <2>. "); + Action action = () => Assert.AreEqual(5.0f, 2.0f, 2.0f); // difference is 3. Delta is 2 + action.Should().Throw().And.Message.Should().Be("Assert.AreEqual failed. Expected a difference no greater than <2> between expected value <5> and actual value <2>. "); } public void FloatAreEqual_ExpectedIsNumeric_ActualIsNumeric_ExpectedAndActualDifferenceGreaterThanDeltaNegative_DeltaIsNumeric_ShouldFail() { - Exception ex = VerifyThrows(() => Assert.AreEqual(2.0f, 5.0f, 2.0f)); // difference is -3. Delta is 2 - Verify(ex.Message == "Assert.AreEqual failed. Expected a difference no greater than <2> between expected value <2> and actual value <5>. "); + Action action = () => Assert.AreEqual(2.0f, 5.0f, 2.0f); // difference is -3. Delta is 2 + action.Should().Throw().And.Message.Should().Be("Assert.AreEqual failed. Expected a difference no greater than <2> between expected value <2> and actual value <5>. "); } public void FloatAreEqual_ExpectedIsNumeric_ActualIsNumeric_ExpectedAndActualDifferenceLessThanDeltaPositive_DeltaIsNumeric_ShouldPass() @@ -581,14 +713,14 @@ public void FloatAreEqual_ExpectedIsNumeric_ActualIsNumeric_ExpectedAndActualDif public void FloatAreEqual_ExpectedIsNumeric_ActualIsNaN_DeltaIsNumeric_ShouldFail() { - Exception ex = VerifyThrows(() => Assert.AreEqual(5.0f, float.NaN, 2.0f)); - Verify(ex.Message == "Assert.AreEqual failed. Expected a difference no greater than <2> between expected value <5> and actual value . "); + Action action = () => Assert.AreEqual(5.0f, float.NaN, 2.0f); + action.Should().Throw().And.Message.Should().Be("Assert.AreEqual failed. Expected a difference no greater than <2> between expected value <5> and actual value . "); } public void FloatAreEqual_ExpectedIsNaN_ActualIsNumeric_DeltaIsNumeric_ShouldFail() { - Exception ex = VerifyThrows(() => Assert.AreEqual(float.NaN, 5.0f, 2.0f)); - Verify(ex.Message == "Assert.AreEqual failed. Expected a difference no greater than <2> between expected value and actual value <5>. "); + Action action = () => Assert.AreEqual(float.NaN, 5.0f, 2.0f); + action.Should().Throw().And.Message.Should().Be("Assert.AreEqual failed. Expected a difference no greater than <2> between expected value and actual value <5>. "); } public void FloatAreEqual_ExpectedIsNaN_ActualIsNaN_DeltaIsNumeric_ShouldPass() @@ -596,137 +728,175 @@ public void FloatAreEqual_ExpectedIsNaN_ActualIsNaN_DeltaIsNumeric_ShouldPass() public void FloatAreNotEqual_ExpectedIsNumeric_ActualIsNumeric_ExpectedAndActualNotEquals_DeltaIsNaN_ShouldFail() { - Exception ex = VerifyThrows(() => Assert.AreNotEqual(1.0f, 2.0f, float.NaN)); - Verify(ex.Message is """ + Action action = () => Assert.AreNotEqual(1.0f, 2.0f, float.NaN); + action.Should().Throw() + .And.Message.Should().BeOneOf( + """ Specified argument was out of the range of valid values. Parameter name: delta - """ or "Specified argument was out of the range of valid values. (Parameter 'delta')"); + """, + "Specified argument was out of the range of valid values. (Parameter 'delta')"); } public void FloatAreNotEqual_ExpectedIsNumeric_ActualIsNumeric_ExpectedAndActualEquals_DeltaIsNaN_ShouldFail() { - Exception ex = VerifyThrows(() => Assert.AreNotEqual(1.0f, 1.0f, float.NaN)); - Verify(ex.Message is """ + Action action = () => Assert.AreNotEqual(1.0f, 1.0f, float.NaN); + action.Should().Throw() + .And.Message.Should().BeOneOf( + """ Specified argument was out of the range of valid values. Parameter name: delta - """ or "Specified argument was out of the range of valid values. (Parameter 'delta')"); + """, + "Specified argument was out of the range of valid values. (Parameter 'delta')"); } public void FloatAreNotEqual_ExpectedIsNaN_ActualIsNumeric_DeltaIsNaN_ShouldFail() { - Exception ex = VerifyThrows(() => Assert.AreNotEqual(float.NaN, 1.0f, float.NaN)); - Verify(ex.Message is """ + Action action = () => Assert.AreNotEqual(float.NaN, 1.0f, float.NaN); + action.Should().Throw().And.Message.Should().BeOneOf( + """ Specified argument was out of the range of valid values. Parameter name: delta - """ or "Specified argument was out of the range of valid values. (Parameter 'delta')"); + """, + "Specified argument was out of the range of valid values. (Parameter 'delta')"); } public void FloatAreNotEqual_ExpectedIsNaN_ActualIsNaN_DeltaIsNaN_ShouldFail() { - Exception ex = VerifyThrows(() => Assert.AreNotEqual(float.NaN, float.NaN, float.NaN)); - Verify(ex.Message is """ + Action action = () => Assert.AreNotEqual(float.NaN, float.NaN, float.NaN); + action.Should().Throw().And.Message.Should().BeOneOf( + """ Specified argument was out of the range of valid values. Parameter name: delta - """ or "Specified argument was out of the range of valid values. (Parameter 'delta')"); + """, + "Specified argument was out of the range of valid values. (Parameter 'delta')"); } public void FloatAreNotEqual_ExpectedIsNumeric_ActualIsNaN_DeltaIsNaN_ShouldFail() { - Exception ex = VerifyThrows(() => Assert.AreNotEqual(1.0f, float.NaN, float.NaN)); - Verify(ex.Message is """ + Action action = () => Assert.AreNotEqual(1.0f, float.NaN, float.NaN); + action.Should().Throw().And.Message.Should().BeOneOf( + """ Specified argument was out of the range of valid values. Parameter name: delta - """ or "Specified argument was out of the range of valid values. (Parameter 'delta')"); + """, + "Specified argument was out of the range of valid values. (Parameter 'delta')"); } public void FloatAreNotEqual_ExpectedIsNumeric_ActualIsNumeric_ExpectedAndActualNotEquals_DeltaIsNegative_ShouldFail() { - Exception ex = VerifyThrows(() => Assert.AreNotEqual(1.0f, 2.0f, -1.0f)); - Verify(ex.Message is """ + Action action = () => Assert.AreNotEqual(1.0f, 2.0f, -1.0f); + action.Should().Throw() + .And.Message.Should().BeOneOf( + """ Specified argument was out of the range of valid values. Parameter name: delta - """ or "Specified argument was out of the range of valid values. (Parameter 'delta')"); + """, + "Specified argument was out of the range of valid values. (Parameter 'delta')"); } public void FloatAreNotEqual_ExpectedIsNumeric_ActualIsNumeric_ExpectedAndActualEquals_DeltaIsNegative_ShouldFail() { - Exception ex = VerifyThrows(() => Assert.AreNotEqual(1.0f, 1.0f, -1.0f)); - Verify(ex.Message is """ + Action action = () => Assert.AreNotEqual(1.0f, 1.0f, -1.0f); + action.Should().Throw().And.Message.Should().BeOneOf( + """ Specified argument was out of the range of valid values. Parameter name: delta - """ or "Specified argument was out of the range of valid values. (Parameter 'delta')"); + """, + "Specified argument was out of the range of valid values. (Parameter 'delta')"); } public void FloatAreNotEqual_ExpectedIsNaN_ActualIsNumeric_DeltaIsNegative_ShouldFail() { - Exception ex = VerifyThrows(() => Assert.AreNotEqual(float.NaN, 1.0f, -1.0f)); - Verify(ex.Message is """ + Action action = () => Assert.AreNotEqual(float.NaN, 1.0f, -1.0f); + action.Should().Throw().And.Message.Should().BeOneOf( + """ Specified argument was out of the range of valid values. Parameter name: delta - """ or "Specified argument was out of the range of valid values. (Parameter 'delta')"); + """, + "Specified argument was out of the range of valid values. (Parameter 'delta')"); } public void FloatAreNotEqual_ExpectedIsNaN_ActualIsNaN_DeltaIsNegative_ShouldFail() { - Exception ex = VerifyThrows(() => Assert.AreNotEqual(float.NaN, float.NaN, -1.0f)); - Verify(ex.Message is """ + Action action = () => Assert.AreNotEqual(float.NaN, float.NaN, -1.0f); + action.Should().Throw().And.Message.Should().BeOneOf( + """ Specified argument was out of the range of valid values. Parameter name: delta - """ or "Specified argument was out of the range of valid values. (Parameter 'delta')"); + """, + "Specified argument was out of the range of valid values. (Parameter 'delta')"); } public void FloatAreNotEqual_ExpectedIsNumeric_ActualIsNaN_DeltaIsNegative_ShouldFail() { - Exception ex = VerifyThrows(() => Assert.AreNotEqual(1.0f, float.NaN, -1.0f)); - Verify(ex.Message is """ + Action action = () => Assert.AreNotEqual(1.0f, float.NaN, -1.0f); + action.Should().Throw().And.Message.Should().BeOneOf( + """ Specified argument was out of the range of valid values. Parameter name: delta - """ or "Specified argument was out of the range of valid values. (Parameter 'delta')"); + """, + "Specified argument was out of the range of valid values. (Parameter 'delta')"); } public void FloatAreNotEqual_ExpectedIsNumeric_ActualIsNumeric_ExpectedAndActualNotEquals_DeltaIsNegativeInf_ShouldFail() { - Exception ex = VerifyThrows(() => Assert.AreNotEqual(1.0f, 2.0f, -1.0f)); - Verify(ex.Message is """ + Action action = () => Assert.AreNotEqual(1.0f, 2.0f, -1.0f); + action.Should().Throw() + .And.Message.Should().BeOneOf( + """ Specified argument was out of the range of valid values. Parameter name: delta - """ or "Specified argument was out of the range of valid values. (Parameter 'delta')"); + """, + "Specified argument was out of the range of valid values. (Parameter 'delta')"); } public void FloatAreNotEqual_ExpectedIsNumeric_ActualIsNumeric_ExpectedAndActualEquals_DeltaIsNegativeInf_ShouldFail() { - Exception ex = VerifyThrows(() => Assert.AreNotEqual(1.0f, 1.0f, float.NegativeInfinity)); - Verify(ex.Message is """ + Action action = () => Assert.AreNotEqual(1.0f, 1.0f, float.NegativeInfinity); + action.Should().Throw() + .And.Message.Should().BeOneOf( + """ Specified argument was out of the range of valid values. Parameter name: delta - """ or "Specified argument was out of the range of valid values. (Parameter 'delta')"); + """, + "Specified argument was out of the range of valid values. (Parameter 'delta')"); } public void FloatAreNotEqual_ExpectedIsNaN_ActualIsNumeric_DeltaIsNegativeInf_ShouldFail() { - Exception ex = VerifyThrows(() => Assert.AreNotEqual(float.NaN, 1.0f, float.NegativeInfinity)); - Verify(ex.Message is """ + Action action = () => Assert.AreNotEqual(float.NaN, 1.0f, float.NegativeInfinity); + action.Should().Throw() + .And.Message.Should().BeOneOf( + """ Specified argument was out of the range of valid values. Parameter name: delta - """ or "Specified argument was out of the range of valid values. (Parameter 'delta')"); + """, + "Specified argument was out of the range of valid values. (Parameter 'delta')"); } public void FloatAreNotEqual_ExpectedIsNaN_ActualIsNaN_DeltaIsNegativeInf_ShouldFail() { - Exception ex = VerifyThrows(() => Assert.AreNotEqual(float.NaN, float.NaN, float.NegativeInfinity)); - Verify(ex.Message is """ + Action action = () => Assert.AreNotEqual(float.NaN, float.NaN, float.NegativeInfinity); + action.Should().Throw() + .And.Message.Should().BeOneOf( + """ Specified argument was out of the range of valid values. Parameter name: delta - """ or "Specified argument was out of the range of valid values. (Parameter 'delta')"); + """, + "Specified argument was out of the range of valid values. (Parameter 'delta')"); } public void FloatAreNotEqual_ExpectedIsNumeric_ActualIsNaN_DeltaIsNegativeInf_ShouldFail() { - Exception ex = VerifyThrows(() => Assert.AreNotEqual(1.0f, float.NaN, float.NegativeInfinity)); - Verify(ex.Message is """ + Action action = () => Assert.AreNotEqual(1.0f, float.NaN, float.NegativeInfinity); + action.Should().Throw() + .And.Message.Should().BeOneOf( + """ Specified argument was out of the range of valid values. Parameter name: delta - """ or "Specified argument was out of the range of valid values. (Parameter 'delta')"); + """, + "Specified argument was out of the range of valid values. (Parameter 'delta')"); } public void FloatAreNotEqual_ExpectedIsNumeric_ActualIsNumeric_ExpectedAndActualDifferenceGreaterThanDeltaPositive_DeltaIsNumeric_ShouldPass() @@ -737,14 +907,14 @@ public void FloatAreNotEqual_ExpectedIsNumeric_ActualIsNumeric_ExpectedAndActual public void FloatAreNotEqual_ExpectedIsNumeric_ActualIsNumeric_ExpectedAndActualDifferenceLessThanDeltaPositive_DeltaIsNumeric_ShouldFail() { - Exception ex = VerifyThrows(() => Assert.AreNotEqual(5.0f, 4.0f, 2.0f)); // difference is 1. Delta is 2 - Verify(ex.Message == "Assert.AreNotEqual failed. Expected a difference greater than <2> between expected value <5> and actual value <4>. "); + Action action = () => Assert.AreNotEqual(5.0f, 4.0f, 2.0f); // difference is 1. Delta is 2 + action.Should().Throw().And.Message.Should().Be("Assert.AreNotEqual failed. Expected a difference greater than <2> between expected value <5> and actual value <4>. "); } public void FloatAreNotEqual_ExpectedIsNumeric_ActualIsNumeric_ExpectedAndActualDifferenceLessThanDeltaNegative_DeltaIsNumeric_ShouldFail() { - Exception ex = VerifyThrows(() => Assert.AreNotEqual(4.0f, 5.0f, 2.0f)); // difference is -1. Delta is 2 - Verify(ex.Message == "Assert.AreNotEqual failed. Expected a difference greater than <2> between expected value <4> and actual value <5>. "); + Action action = () => Assert.AreNotEqual(4.0f, 5.0f, 2.0f); // difference is -1. Delta is 2 + action.Should().Throw().And.Message.Should().Be("Assert.AreNotEqual failed. Expected a difference greater than <2> between expected value <4> and actual value <5>. "); } public void FloatAreNotEqual_ExpectedIsNumeric_ActualIsNaN_DeltaIsNumeric_ShouldPass() => Assert.AreNotEqual(5.0f, float.NaN, 2.0f); @@ -754,155 +924,199 @@ public void FloatAreNotEqual_ExpectedIsNaN_ActualIsNumeric_DeltaIsNumeric_Should public void FloatAreNotEqual_ExpectedIsNaN_ActualIsNaN_DeltaIsNumeric_ShouldFail() { - Exception ex = VerifyThrows(() => Assert.AreNotEqual(float.NaN, float.NaN, 2.0f)); - Verify(ex.Message == "Assert.AreNotEqual failed. Expected a difference greater than <2> between expected value and actual value . "); + Action action = () => Assert.AreNotEqual(float.NaN, float.NaN, 2.0f); + action.Should().Throw().And.Message.Should().Be("Assert.AreNotEqual failed. Expected a difference greater than <2> between expected value and actual value . "); } public void DoubleAreEqual_ExpectedIsNumeric_ActualIsNumeric_ExpectedAndActualNotEquals_DeltaIsNaN_ShouldFail() { - Exception ex = VerifyThrows(() => Assert.AreEqual(1.0d, 2.0d, double.NaN)); - Verify(ex.Message is """ + Action action = () => Assert.AreEqual(1.0d, 2.0d, double.NaN); + action.Should().Throw().And.Message.Should().BeOneOf( + """ Specified argument was out of the range of valid values. Parameter name: delta - """ or "Specified argument was out of the range of valid values. (Parameter 'delta')"); + """, + "Specified argument was out of the range of valid values. (Parameter 'delta')"); } public void DoubleAreEqual_ExpectedIsNumeric_ActualIsNumeric_ExpectedAndActualEquals_DeltaIsNaN_ShouldFail() { - Exception ex = VerifyThrows(() => Assert.AreEqual(1.0d, 1.0d, double.NaN)); - Verify(ex.Message is """ + Action action = () => Assert.AreEqual(1.0d, 1.0d, double.NaN); + action.Should().Throw().And.Message.Should().BeOneOf( + """ Specified argument was out of the range of valid values. Parameter name: delta - """ or "Specified argument was out of the range of valid values. (Parameter 'delta')"); + """, + "Specified argument was out of the range of valid values. (Parameter 'delta')"); } public void DoubleAreEqual_ExpectedIsNaN_ActualIsNumeric_DeltaIsNaN_ShouldFail() { - Exception ex = VerifyThrows(() => Assert.AreEqual(double.NaN, 1.0d, double.NaN)); - Verify(ex.Message is """ + Action action = () => Assert.AreEqual(double.NaN, 1.0d, double.NaN); + action.Should().Throw() + .And.Message.Should().BeOneOf( + """ Specified argument was out of the range of valid values. Parameter name: delta - """ or "Specified argument was out of the range of valid values. (Parameter 'delta')"); + """, + "Specified argument was out of the range of valid values. (Parameter 'delta')"); } public void DoubleAreEqual_ExpectedIsNaN_ActualIsNaN_DeltaIsNaN_ShouldFail() { - Exception ex = VerifyThrows(() => Assert.AreEqual(double.NaN, double.NaN, double.NaN)); - Verify(ex.Message is """ + Action action = () => Assert.AreEqual(double.NaN, double.NaN, double.NaN); + action.Should().Throw() + .And.Message.Should().BeOneOf( + """ Specified argument was out of the range of valid values. Parameter name: delta - """ or "Specified argument was out of the range of valid values. (Parameter 'delta')"); + """, + "Specified argument was out of the range of valid values. (Parameter 'delta')"); } public void DoubleAreEqual_ExpectedIsNumeric_ActualIsNaN_DeltaIsNaN_ShouldFail() { - Exception ex = VerifyThrows(() => Assert.AreEqual(1.0d, double.NaN, double.NaN)); - Verify(ex.Message is """ + Action action = () => Assert.AreEqual(1.0d, double.NaN, double.NaN); + action.Should().Throw() + .And.Message.Should().BeOneOf( + """ Specified argument was out of the range of valid values. Parameter name: delta - """ or "Specified argument was out of the range of valid values. (Parameter 'delta')"); + """, + "Specified argument was out of the range of valid values. (Parameter 'delta')"); } public void DoubleAreEqual_ExpectedIsNumeric_ActualIsNumeric_ExpectedAndActualNotEquals_DeltaIsNegative_ShouldFail() { - Exception ex = VerifyThrows(() => Assert.AreEqual(1.0d, 2.0d, -1.0d)); - Verify(ex.Message is """ + Action action = () => Assert.AreEqual(1.0d, 2.0d, -1.0d); + action.Should().Throw() + .And.Message.Should().BeOneOf( + """ Specified argument was out of the range of valid values. Parameter name: delta - """ or "Specified argument was out of the range of valid values. (Parameter 'delta')"); + """, + "Specified argument was out of the range of valid values. (Parameter 'delta')"); } public void DoubleAreEqual_ExpectedIsNumeric_ActualIsNumeric_ExpectedAndActualEquals_DeltaIsNegative_ShouldFail() { - Exception ex = VerifyThrows(() => Assert.AreEqual(1.0d, 1.0d, -1.0d)); - Verify(ex.Message is """ + Action action = () => Assert.AreEqual(1.0d, 1.0d, -1.0d); + action.Should().Throw() + .And.Message.Should().BeOneOf( + """ Specified argument was out of the range of valid values. Parameter name: delta - """ or "Specified argument was out of the range of valid values. (Parameter 'delta')"); + """, + "Specified argument was out of the range of valid values. (Parameter 'delta')"); } public void DoubleAreEqual_ExpectedIsNaN_ActualIsNumeric_DeltaIsNegative_ShouldFail() { - Exception ex = VerifyThrows(() => Assert.AreEqual(double.NaN, 1.0d, -1.0d)); - Verify(ex.Message is """ + Action action = () => Assert.AreEqual(double.NaN, 1.0d, -1.0d); + action.Should().Throw() + .And.Message.Should().BeOneOf( + """ Specified argument was out of the range of valid values. Parameter name: delta - """ or "Specified argument was out of the range of valid values. (Parameter 'delta')"); + """, + "Specified argument was out of the range of valid values. (Parameter 'delta')"); } public void DoubleAreEqual_ExpectedIsNaN_ActualIsNaN_DeltaIsNegative_ShouldFail() { - Exception ex = VerifyThrows(() => Assert.AreEqual(double.NaN, double.NaN, -1.0d)); - Verify(ex.Message is """ + Action action = () => Assert.AreEqual(double.NaN, double.NaN, -1.0d); + action.Should().Throw() + .And.Message.Should().BeOneOf( + """ Specified argument was out of the range of valid values. Parameter name: delta - """ or "Specified argument was out of the range of valid values. (Parameter 'delta')"); + """, + "Specified argument was out of the range of valid values. (Parameter 'delta')"); } public void DoubleAreEqual_ExpectedIsNumeric_ActualIsNaN_DeltaIsNegative_ShouldFail() { - Exception ex = VerifyThrows(() => Assert.AreEqual(1.0d, double.NaN, -1.0d)); - Verify(ex.Message is """ + Action action = () => Assert.AreEqual(1.0d, double.NaN, -1.0d); + action.Should().Throw() + .And.Message.Should().BeOneOf( + """ Specified argument was out of the range of valid values. Parameter name: delta - """ or "Specified argument was out of the range of valid values. (Parameter 'delta')"); + """, + "Specified argument was out of the range of valid values. (Parameter 'delta')"); } public void DoubleAreEqual_ExpectedIsNumeric_ActualIsNumeric_ExpectedAndActualNotEquals_DeltaIsNegativeInf_ShouldFail() { - Exception ex = VerifyThrows(() => Assert.AreEqual(1.0d, 2.0d, double.NegativeInfinity)); - Verify(ex.Message is """ + Action action = () => Assert.AreEqual(1.0d, 2.0d, double.NegativeInfinity); + action.Should().Throw() + .And.Message.Should().BeOneOf( + """ Specified argument was out of the range of valid values. Parameter name: delta - """ or "Specified argument was out of the range of valid values. (Parameter 'delta')"); + """, + "Specified argument was out of the range of valid values. (Parameter 'delta')"); } public void DoubleAreEqual_ExpectedIsNumeric_ActualIsNumeric_ExpectedAndActualEquals_DeltaIsNegativeInf_ShouldFail() { - Exception ex = VerifyThrows(() => Assert.AreEqual(1.0d, 1.0d, double.NegativeInfinity)); - Verify(ex.Message is """ + Action action = () => Assert.AreEqual(1.0d, 1.0d, double.NegativeInfinity); + action.Should().Throw() + .And.Message.Should().BeOneOf( + """ Specified argument was out of the range of valid values. Parameter name: delta - """ or "Specified argument was out of the range of valid values. (Parameter 'delta')"); + """, + "Specified argument was out of the range of valid values. (Parameter 'delta')"); } public void DoubleAreEqual_ExpectedIsNaN_ActualIsNumeric_DeltaIsNegativeInf_ShouldFail() { - Exception ex = VerifyThrows(() => Assert.AreEqual(double.NaN, 1.0d, double.NegativeInfinity)); - Verify(ex.Message is """ + Action action = () => Assert.AreEqual(double.NaN, 1.0d, double.NegativeInfinity); + action.Should().Throw() + .And.Message.Should().BeOneOf( + """ Specified argument was out of the range of valid values. Parameter name: delta - """ or "Specified argument was out of the range of valid values. (Parameter 'delta')"); + """, + "Specified argument was out of the range of valid values. (Parameter 'delta')"); } public void DoubleAreEqual_ExpectedIsNaN_ActualIsNaN_DeltaIsNegativeInf_ShouldFail() { - Exception ex = VerifyThrows(() => Assert.AreEqual(double.NaN, double.NaN, double.NegativeInfinity)); - Verify(ex.Message is """ + Action action = () => Assert.AreEqual(double.NaN, double.NaN, double.NegativeInfinity); + action.Should().Throw() + .And.Message.Should().BeOneOf( + """ Specified argument was out of the range of valid values. Parameter name: delta - """ or "Specified argument was out of the range of valid values. (Parameter 'delta')"); + """, "Specified argument was out of the range of valid values. (Parameter 'delta')"); } public void DoubleAreEqual_ExpectedIsNumeric_ActualIsNaN_DeltaIsNegativeInf_ShouldFail() { - Exception ex = VerifyThrows(() => Assert.AreEqual(1.0d, double.NaN, double.NegativeInfinity)); - Verify(ex.Message is """ + Action action = () => Assert.AreEqual(1.0d, double.NaN, double.NegativeInfinity); + action.Should().Throw() + .And.Message.Should().BeOneOf( + """ Specified argument was out of the range of valid values. Parameter name: delta - """ or "Specified argument was out of the range of valid values. (Parameter 'delta')"); + """, + "Specified argument was out of the range of valid values. (Parameter 'delta')"); } public void DoubleAreEqual_ExpectedIsNumeric_ActualIsNumeric_ExpectedAndActualDifferenceGreaterThanDeltaPositive_DeltaIsNumeric_ShouldFail() { - Exception ex = VerifyThrows(() => Assert.AreEqual(5.0d, 2.0d, 2.0d)); // difference is 3. Delta is 2 - Verify(ex.Message == "Assert.AreEqual failed. Expected a difference no greater than <2> between expected value <5> and actual value <2>. "); + Action action = () => Assert.AreEqual(5.0d, 2.0d, 2.0d); // difference is 3. Delta is 2 + action.Should().Throw().And + .Message.Should().Be("Assert.AreEqual failed. Expected a difference no greater than <2> between expected value <5> and actual value <2>. "); } public void DoubleAreEqual_ExpectedIsNumeric_ActualIsNumeric_ExpectedAndActualDifferenceGreaterThanDeltaNegative_DeltaIsNumeric_ShouldFail() { - Exception ex = VerifyThrows(() => Assert.AreEqual(2.0d, 5.0d, 2.0d)); // difference is -3. Delta is 2 - Verify(ex.Message == "Assert.AreEqual failed. Expected a difference no greater than <2> between expected value <2> and actual value <5>. "); + Action action = () => Assert.AreEqual(2.0d, 5.0d, 2.0d); // difference is -3. Delta is 2 + action.Should().Throw().And + .Message.Should().Be("Assert.AreEqual failed. Expected a difference no greater than <2> between expected value <2> and actual value <5>. "); } public void DoubleAreEqual_ExpectedIsNumeric_ActualIsNumeric_ExpectedAndActualDifferenceLessThanDeltaPositive_DeltaIsNumeric_ShouldPass() @@ -913,14 +1127,16 @@ public void DoubleAreEqual_ExpectedIsNumeric_ActualIsNumeric_ExpectedAndActualDi public void DoubleAreEqual_ExpectedIsNumeric_ActualIsNaN_DeltaIsNumeric_ShouldFail() { - Exception ex = VerifyThrows(() => Assert.AreEqual(5.0d, double.NaN, 2.0d)); - Verify(ex.Message == "Assert.AreEqual failed. Expected a difference no greater than <2> between expected value <5> and actual value . "); + Action action = () => Assert.AreEqual(5.0d, double.NaN, 2.0d); + action.Should().Throw().And + .Message.Should().Be("Assert.AreEqual failed. Expected a difference no greater than <2> between expected value <5> and actual value . "); } public void DoubleAreEqual_ExpectedIsNaN_ActualIsNumeric_DeltaIsNumeric_ShouldFail() { - Exception ex = VerifyThrows(() => Assert.AreEqual(double.NaN, 5.0d, 2.0d)); - Verify(ex.Message == "Assert.AreEqual failed. Expected a difference no greater than <2> between expected value and actual value <5>. "); + Action action = () => Assert.AreEqual(double.NaN, 5.0d, 2.0d); + action.Should().Throw().And + .Message.Should().Be("Assert.AreEqual failed. Expected a difference no greater than <2> between expected value and actual value <5>. "); } public void DoubleAreEqual_ExpectedIsNaN_ActualIsNaN_DeltaIsNumeric_ShouldPass() @@ -928,137 +1144,182 @@ public void DoubleAreEqual_ExpectedIsNaN_ActualIsNaN_DeltaIsNumeric_ShouldPass() public void DoubleAreNotEqual_ExpectedIsNumeric_ActualIsNumeric_ExpectedAndActualNotEquals_DeltaIsNaN_ShouldFail() { - Exception ex = VerifyThrows(() => Assert.AreNotEqual(1.0d, 2.0d, double.NaN)); - Verify(ex.Message is """ + Action action = () => Assert.AreNotEqual(1.0d, 2.0d, double.NaN); + action.Should().Throw() + .And.Message.Should().BeOneOf( + """ Specified argument was out of the range of valid values. Parameter name: delta - """ or "Specified argument was out of the range of valid values. (Parameter 'delta')"); + """, + "Specified argument was out of the range of valid values. (Parameter 'delta')"); } public void DoubleAreNotEqual_ExpectedIsNumeric_ActualIsNumeric_ExpectedAndActualEquals_DeltaIsNaN_ShouldFail() { - Exception ex = VerifyThrows(() => Assert.AreNotEqual(1.0d, 1.0d, double.NaN)); - Verify(ex.Message is """ + Action action = () => Assert.AreNotEqual(1.0d, 1.0d, double.NaN); + action.Should().Throw() + .And.Message.Should().BeOneOf( + """ Specified argument was out of the range of valid values. Parameter name: delta - """ or "Specified argument was out of the range of valid values. (Parameter 'delta')"); + """, + "Specified argument was out of the range of valid values. (Parameter 'delta')"); } public void DoubleAreNotEqual_ExpectedIsNaN_ActualIsNumeric_DeltaIsNaN_ShouldFail() { - Exception ex = VerifyThrows(() => Assert.AreNotEqual(double.NaN, 1.0d, double.NaN)); - Verify(ex.Message is """ + Action action = () => Assert.AreNotEqual(double.NaN, 1.0d, double.NaN); + action.Should().Throw() + .And.Message.Should().BeOneOf( + """ Specified argument was out of the range of valid values. Parameter name: delta - """ or "Specified argument was out of the range of valid values. (Parameter 'delta')"); + """, + "Specified argument was out of the range of valid values. (Parameter 'delta')"); } public void DoubleAreNotEqual_ExpectedIsNaN_ActualIsNaN_DeltaIsNaN_ShouldFail() { - Exception ex = VerifyThrows(() => Assert.AreNotEqual(double.NaN, double.NaN, double.NaN)); - Verify(ex.Message is """ + Action action = () => Assert.AreNotEqual(double.NaN, double.NaN, double.NaN); + action.Should().Throw() + .And.Message.Should().BeOneOf( + """ Specified argument was out of the range of valid values. Parameter name: delta - """ or "Specified argument was out of the range of valid values. (Parameter 'delta')"); + """, + "Specified argument was out of the range of valid values. (Parameter 'delta')"); } public void DoubleAreNotEqual_ExpectedIsNumeric_ActualIsNaN_DeltaIsNaN_ShouldFail() { - Exception ex = VerifyThrows(() => Assert.AreNotEqual(1.0d, double.NaN, double.NaN)); - Verify(ex.Message is """ + Action action = () => Assert.AreNotEqual(1.0d, double.NaN, double.NaN); + action.Should().Throw() + .And.Message.Should().BeOneOf( + """ Specified argument was out of the range of valid values. Parameter name: delta - """ or "Specified argument was out of the range of valid values. (Parameter 'delta')"); + """, + "Specified argument was out of the range of valid values. (Parameter 'delta')"); } public void DoubleAreNotEqual_ExpectedIsNumeric_ActualIsNumeric_ExpectedAndActualNotEquals_DeltaIsNegative_ShouldFail() { - Exception ex = VerifyThrows(() => Assert.AreNotEqual(1.0d, 2.0d, -1.0d)); - Verify(ex.Message is """ + Action action = () => Assert.AreNotEqual(1.0d, 2.0d, -1.0d); + action.Should().Throw() + .And.Message.Should().BeOneOf( + """ Specified argument was out of the range of valid values. Parameter name: delta - """ or "Specified argument was out of the range of valid values. (Parameter 'delta')"); + """, + "Specified argument was out of the range of valid values. (Parameter 'delta')"); } public void DoubleAreNotEqual_ExpectedIsNumeric_ActualIsNumeric_ExpectedAndActualEquals_DeltaIsNegative_ShouldFail() { - Exception ex = VerifyThrows(() => Assert.AreNotEqual(1.0d, 1.0d, -1.0d)); - Verify(ex.Message is """ + Action action = () => Assert.AreNotEqual(1.0d, 1.0d, -1.0d); + action.Should().Throw() + .And.Message.Should().BeOneOf( + """ Specified argument was out of the range of valid values. Parameter name: delta - """ or "Specified argument was out of the range of valid values. (Parameter 'delta')"); + """, + "Specified argument was out of the range of valid values. (Parameter 'delta')"); } public void DoubleAreNotEqual_ExpectedIsNaN_ActualIsNumeric_DeltaIsNegative_ShouldFail() { - Exception ex = VerifyThrows(() => Assert.AreNotEqual(double.NaN, 1.0d, -1.0d)); - Verify(ex.Message is """ + Action action = () => Assert.AreNotEqual(double.NaN, 1.0d, -1.0d); + action.Should().Throw() + .And.Message.Should().BeOneOf( + """ Specified argument was out of the range of valid values. Parameter name: delta - """ or "Specified argument was out of the range of valid values. (Parameter 'delta')"); + """, + "Specified argument was out of the range of valid values. (Parameter 'delta')"); } public void DoubleAreNotEqual_ExpectedIsNaN_ActualIsNaN_DeltaIsNegative_ShouldFail() { - Exception ex = VerifyThrows(() => Assert.AreNotEqual(double.NaN, double.NaN, -1.0d)); - Verify(ex.Message is """ + Action action = () => Assert.AreNotEqual(double.NaN, double.NaN, -1.0d); + action.Should().Throw() + .And.Message.Should().BeOneOf( + """ Specified argument was out of the range of valid values. Parameter name: delta - """ or "Specified argument was out of the range of valid values. (Parameter 'delta')"); + """, + "Specified argument was out of the range of valid values. (Parameter 'delta')"); } public void DoubleAreNotEqual_ExpectedIsNumeric_ActualIsNaN_DeltaIsNegative_ShouldFail() { - Exception ex = VerifyThrows(() => Assert.AreNotEqual(1.0d, double.NaN, -1.0d)); - Verify(ex.Message is """ + Action action = () => Assert.AreNotEqual(1.0d, double.NaN, -1.0d); + action.Should().Throw() + .And.Message.Should().BeOneOf( + """ Specified argument was out of the range of valid values. Parameter name: delta - """ or "Specified argument was out of the range of valid values. (Parameter 'delta')"); + """, + "Specified argument was out of the range of valid values. (Parameter 'delta')"); } public void DoubleAreNotEqual_ExpectedIsNumeric_ActualIsNumeric_ExpectedAndActualNotEquals_DeltaIsNegativeInf_ShouldFail() { - Exception ex = VerifyThrows(() => Assert.AreNotEqual(1.0d, 2.0d, double.NegativeInfinity)); - Verify(ex.Message is """ + Action action = () => Assert.AreNotEqual(1.0d, 2.0d, double.NegativeInfinity); + action.Should().Throw() + .And.Message.Should().BeOneOf( + """ Specified argument was out of the range of valid values. Parameter name: delta - """ or "Specified argument was out of the range of valid values. (Parameter 'delta')"); + """, + "Specified argument was out of the range of valid values. (Parameter 'delta')"); } public void DoubleAreNotEqual_ExpectedIsNumeric_ActualIsNumeric_ExpectedAndActualEquals_DeltaIsNegativeInf_ShouldFail() { - Exception ex = VerifyThrows(() => Assert.AreNotEqual(1.0d, 1.0d, double.NegativeInfinity)); - Verify(ex.Message is """ + Action action = () => Assert.AreNotEqual(1.0d, 1.0d, double.NegativeInfinity); + action.Should().Throw() + .And.Message.Should().BeOneOf( + """ Specified argument was out of the range of valid values. Parameter name: delta - """ or "Specified argument was out of the range of valid values. (Parameter 'delta')"); + """, + "Specified argument was out of the range of valid values. (Parameter 'delta')"); } public void DoubleAreNotEqual_ExpectedIsNaN_ActualIsNumeric_DeltaIsNegativeInf_ShouldFail() { - Exception ex = VerifyThrows(() => Assert.AreNotEqual(double.NaN, 1.0d, double.NegativeInfinity)); - Verify(ex.Message is """ + Action action = () => Assert.AreNotEqual(double.NaN, 1.0d, double.NegativeInfinity); + action.Should().Throw() + .And.Message.Should().BeOneOf( + """ Specified argument was out of the range of valid values. Parameter name: delta - """ or "Specified argument was out of the range of valid values. (Parameter 'delta')"); + """, + "Specified argument was out of the range of valid values. (Parameter 'delta')"); } public void DoubleAreNotEqual_ExpectedIsNaN_ActualIsNaN_DeltaIsNegativeInf_ShouldFail() { - Exception ex = VerifyThrows(() => Assert.AreNotEqual(double.NaN, double.NaN, double.NegativeInfinity)); - Verify(ex.Message is """ - Specified argument was out of the range of valid values. - Parameter name: delta - """ or "Specified argument was out of the range of valid values. (Parameter 'delta')"); + Action action = () => Assert.AreNotEqual(double.NaN, double.NaN, double.NegativeInfinity); + action.Should().Throw().And + .Message.Should().BeOneOf( + """ + Specified argument was out of the range of valid values. + Parameter name: delta + """, + "Specified argument was out of the range of valid values. (Parameter 'delta')"); } public void DoubleAreNotEqual_ExpectedIsNumeric_ActualIsNaN_DeltaIsNegativeInf_ShouldFail() { - Exception ex = VerifyThrows(() => Assert.AreNotEqual(1.0d, double.NaN, double.NegativeInfinity)); - Verify(ex.Message is """ - Specified argument was out of the range of valid values. - Parameter name: delta - """ or "Specified argument was out of the range of valid values. (Parameter 'delta')"); + Action action = () => Assert.AreNotEqual(1.0d, double.NaN, double.NegativeInfinity); + action.Should().Throw().And + .Message.Should().BeOneOf( + """ + Specified argument was out of the range of valid values. + Parameter name: delta + """, + "Specified argument was out of the range of valid values. (Parameter 'delta')"); } public void DoubleAreNotEqual_ExpectedIsNumeric_ActualIsNumeric_ExpectedAndActualDifferenceGreaterThanDeltaPositive_DeltaIsNumeric_ShouldPass() @@ -1069,14 +1330,16 @@ public void DoubleAreNotEqual_ExpectedIsNumeric_ActualIsNumeric_ExpectedAndActua public void DoubleAreNotEqual_ExpectedIsNumeric_ActualIsNumeric_ExpectedAndActualDifferenceLessThanDeltaPositive_DeltaIsNumeric_ShouldFail() { - Exception ex = VerifyThrows(() => Assert.AreNotEqual(5.0d, 4.0d, 2.0d)); // difference is 1. Delta is 2 - Verify(ex.Message == "Assert.AreNotEqual failed. Expected a difference greater than <2> between expected value <5> and actual value <4>. "); + Action action = () => Assert.AreNotEqual(5.0d, 4.0d, 2.0d); // difference is 1. Delta is 2 + action.Should().Throw().And + .Message.Should().Be("Assert.AreNotEqual failed. Expected a difference greater than <2> between expected value <5> and actual value <4>. "); } public void DoubleAreNotEqual_ExpectedIsNumeric_ActualIsNumeric_ExpectedAndActualDifferenceLessThanDeltaNegative_DeltaIsNumeric_ShouldFail() { - Exception ex = VerifyThrows(() => Assert.AreNotEqual(4.0d, 5.0d, 2.0d)); // difference is -1. Delta is 2 - Verify(ex.Message == "Assert.AreNotEqual failed. Expected a difference greater than <2> between expected value <4> and actual value <5>. "); + Action action = () => Assert.AreNotEqual(4.0d, 5.0d, 2.0d); // difference is -1. Delta is 2 + action.Should().Throw().And + .Message.Should().Be("Assert.AreNotEqual failed. Expected a difference greater than <2> between expected value <4> and actual value <5>. "); } public void DoubleAreNotEqual_ExpectedIsNumeric_ActualIsNaN_DeltaIsNumeric_ShouldPass() => Assert.AreNotEqual(5.0d, double.NaN, 2.0d); @@ -1086,8 +1349,9 @@ public void DoubleAreNotEqual_ExpectedIsNaN_ActualIsNumeric_DeltaIsNumeric_Shoul public void DoubleAreNotEqual_ExpectedIsNaN_ActualIsNaN_DeltaIsNumeric_ShouldFail() { - Exception ex = VerifyThrows(() => Assert.AreNotEqual(double.NaN, double.NaN, 2.0d)); - Verify(ex.Message == "Assert.AreNotEqual failed. Expected a difference greater than <2> between expected value and actual value . "); + Action action = () => Assert.AreNotEqual(double.NaN, double.NaN, 2.0d); + action.Should().Throw().And + .Message.Should().Be("Assert.AreNotEqual failed. Expected a difference greater than <2> between expected value and actual value . "); } private class TypeOverridesEquals @@ -1140,4 +1404,226 @@ public bool Equals(A? other) public override int GetHashCode() => Id.GetHashCode() + 1234; } + + public void AreEqualStringDifferenceAtBeginning() + { + Action action = () => Assert.AreEqual("baaa", "aaaa"); + action.Should().Throw() + .And.Message.Should().Be(""" + Assert.AreEqual failed. String lengths are both 4 but differ at index 0. + Expected: "baaa" + But was: "aaaa" + -----------^ + """); + } + + public void AreEqualStringDifferenceAtEnd() + { + Action action = () => Assert.AreEqual("aaaa", "aaab"); + action.Should().Throw() + .And.Message.Should().Be(""" + Assert.AreEqual failed. String lengths are both 4 but differ at index 3. + Expected: "aaaa" + But was: "aaab" + --------------^ + """); + } + + public void AreEqualStringWithSpecialCharactersShouldEscape() + { + Action action = () => Assert.AreEqual("aa\ta", "aa a"); + action.Should().Throw() + .And.Message.Should().Be(""" + Assert.AreEqual failed. String lengths are both 4 but differ at index 2. + Expected: "aa␉a" + But was: "aa a" + -------------^ + """); + } + + public void AreEqualLongStringsShouldTruncateAndShowContext() + { + string expected = new string('a', 100) + "b" + new string('c', 100); + string actual = new string('a', 100) + "d" + new string('c', 100); + + Action action = () => Assert.AreEqual(expected, actual); + action.Should().Throw() + .And.Message.Should().Be(""" + Assert.AreEqual failed. String lengths are both 201 but differ at index 100. + Expected: "...aaaaaaaaaaaaaaaaaabcccccccccccccccc..." + But was: "...aaaaaaaaaaaaaaaaaadcccccccccccccccc..." + --------------------------------^ + """); + } + + public void AreEqualStringWithCultureShouldUseEnhancedMessage() + { + Action action = () => Assert.AreEqual("aaaa", "aaab", false, CultureInfo.InvariantCulture); + action.Should().Throw() + .And.Message.Should().Be(""" + Assert.AreEqual failed. String lengths are both 4 but differ at index 3. + Expected: "aaaa" + But was: "aaab" + --------------^ + """); + } + + public void AreEqualStringWithDifferentLength() + { + Action action = () => Assert.AreEqual("aaaa", "aaa"); + action.Should().Throw() + .And.Message.Should().Be(""" + Assert.AreEqual failed. Expected string length 4 but was 3. + Expected: "aaaa" + But was: "aaa" + --------------^ + """); + } + + public void AreEqualShorterExpectedString() + { + Action action = () => Assert.AreEqual("aaa", "aaab"); + action.Should().Throw() + .And.Message.Should().Be(""" + Assert.AreEqual failed. Expected string length 3 but was 4. + Expected: "aaa" + But was: "aaab" + --------------^ + """); + } + + public void AreEqualStringWithUserMessage() + { + Action action = () => Assert.AreEqual("aaaa", "aaab", "My custom message"); + action.Should().Throw() + .And.Message.Should().Be(""" + Assert.AreEqual failed. String lengths are both 4 but differ at index 3. My custom message + Expected: "aaaa" + But was: "aaab" + --------------^ + """); + } + + public void AreEqualStringWithEmojis() + { + Action action = () => Assert.AreEqual("🥰", "aaab"); + action.Should().Throw().And + .Message.Should().Be(""" + Assert.AreEqual failed. Expected string length 2 but was 4. + Expected: "🥰" + But was: "aaab" + -----------^ + """); + } + + public void CreateStringPreviews_DiffPointsToCorrectPlaceInNonShortenedString() + { + int preview = 9; + int length = 1; + int diffIndex = 0; + string stringPreview = FormatStringPreview(StringPreviewHelper.CreateStringPreviews(DigitString(length, diffIndex), DigitString(length, diffIndex), diffIndex, preview)); + stringPreview.Should().Be(""" + "X" + "X" + _^ + """); + } + + public void CreateStringPreviews_DiffPointsToCorrectPlaceInShortenedStringWithEndCut() + { + int preview = 9; + int length = preview + 10; + int diffIndex = 0; + string stringPreview = FormatStringPreview(StringPreviewHelper.CreateStringPreviews(DigitString(length, diffIndex), DigitString(length, diffIndex), diffIndex, preview)); + stringPreview.Should().Be(""" + "X12345..." + "X12345..." + _^ + """); + } + + public void CreateStringPreviews_DiffPointsToCorrectPlaceInShortenedStringWithStartCut() + { + int preview = 9; + int length = 10; + int diffIndex = 9; + string stringPreview = FormatStringPreview(StringPreviewHelper.CreateStringPreviews(DigitString(length, diffIndex), DigitString(length, diffIndex), diffIndex: diffIndex, preview)); + stringPreview.Should().Be(""" + "...45678X" + "...45678X" + _________^ + """); + } + + public void CreateStringPreviews_ShowWholeStringWhenDifferenceIsAtTheEndAndJustOneStringDoesNotFit() + { + int preview = 21; + int length = 50; + int diffIndex = 16; + string stringPreview = FormatStringPreview(StringPreviewHelper.CreateStringPreviews(DigitString(preview, diffIndex), DigitString(length, diffIndex), diffIndex: diffIndex, preview)); + stringPreview.Should().Be(""" + "0123456789012345X7890" + "0123456789012345X7..." + _________________^ + """); + } + + public void CreateStringPreviews_MakeSureWeDontPointToEndEllipsis() + { + // We will mask last 3 chars of the string, so we need to make sure that the diff index is not pointing to the end ellipsis. + int preview = 25; + int length = 50; + int diffIndex = 24; + + string stringPreview = FormatStringPreview(StringPreviewHelper.CreateStringPreviews(DigitString(preview, diffIndex), DigitString(length, diffIndex), diffIndex: diffIndex, preview)); + stringPreview.Should().Be(""" + "...8901234567890123X" + "...8901234567890123X56..." + ____________________^ + """); + } + + public void CreateStringPreviews_DiffPointsAfterLastCharacterWhenStringsAreAllTheSameCharactersUntilTheEndOfTheShorterOne() + { + int preview = 9; + int diffIndex = 3; + string stringPreview = FormatStringPreview(StringPreviewHelper.CreateStringPreviews("aaa", "aaaX", diffIndex, preview)); + stringPreview.Should().Be(""" + "aaa" + "aaaX" + ____^ + """); + } + + private string FormatStringPreview(Tuple tuple) + => $""" + "{tuple.Item1}" + "{tuple.Item2}" + {new string('_', tuple.Item3 + 1)}{'^'} + """; + + private static string DigitString(int length, int diffIndex) + { + const string digits = "0123456789"; + if (length <= 0) + { + return string.Empty; + } + + var result = new StringBuilder(length); + for (int i = 0; i < length; i++) + { + if (i == diffIndex) + { + // Use 'X' to indicate a difference should be at this index. + // To make it easier to see where the arrow should point, even though both strings are the same (we provide the diff index externally). + result.Append('X'); + continue; + } + + result.Append(digits[i % digits.Length]); + } + + return result.ToString(); + } } diff --git a/test/UnitTests/TestFramework.UnitTests/Assertions/AssertTests.AreSame.cs b/test/UnitTests/TestFramework.UnitTests/Assertions/AssertTests.AreSame.cs index 853f3c1f37..57d9a643c4 100644 --- a/test/UnitTests/TestFramework.UnitTests/Assertions/AssertTests.AreSame.cs +++ b/test/UnitTests/TestFramework.UnitTests/Assertions/AssertTests.AreSame.cs @@ -1,6 +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 AwesomeAssertions; + namespace Microsoft.VisualStudio.TestPlatform.TestFramework.UnitTests; public partial class AssertTests @@ -13,8 +15,8 @@ public void AreSame_PassSameObject_ShouldPass() public void AreSame_PassDifferentObject_ShouldFail() { - Exception ex = VerifyThrows(() => Assert.AreSame(new object(), new object())); - Verify(ex.Message == "Assert.AreSame failed. "); + Action action = () => Assert.AreSame(new object(), new object()); + action.Should().Throw().And.Message.Should().Be("Assert.AreSame failed. "); } public void AreSame_StringMessage_PassSameObject_ShouldPass() @@ -25,24 +27,24 @@ public void AreSame_StringMessage_PassSameObject_ShouldPass() public void AreSame_StringMessage_PassDifferentObject_ShouldFail() { - Exception ex = VerifyThrows(() => Assert.AreSame(new object(), new object(), "User-provided message")); - Verify(ex.Message == "Assert.AreSame failed. User-provided message"); + Action action = () => Assert.AreSame(new object(), new object(), "User-provided message"); + action.Should().Throw().And.Message.Should().Be("Assert.AreSame failed. User-provided message"); } public void AreSame_InterpolatedString_PassSameObject_ShouldPass() { DummyClassTrackingToStringCalls o = new(); Assert.AreSame(o, o, $"User-provided message: {o}"); - Verify(!o.WasToStringCalled); + o.WasToStringCalled.Should().BeFalse(); } public async Task AreSame_InterpolatedString_PassDifferentObject_ShouldFail() { DummyClassTrackingToStringCalls o = new(); DateTime dateTime = DateTime.Now; - Exception ex = await VerifyThrowsAsync(async () => Assert.AreSame(new object(), new object(), $"User-provided message. {o}, {o,35}, {await GetHelloStringAsync()}, {new DummyIFormattable()}, {dateTime:tt}, {dateTime,5:tt}")); - Verify(ex.Message == $"Assert.AreSame failed. User-provided message. DummyClassTrackingToStringCalls, DummyClassTrackingToStringCalls, Hello, DummyIFormattable.ToString(), {string.Format(null, "{0:tt}", dateTime)}, {string.Format(null, "{0,5:tt}", dateTime)}"); - Verify(o.WasToStringCalled); + Func action = async () => Assert.AreSame(new object(), new object(), $"User-provided message. {o}, {o,35}, {await GetHelloStringAsync()}, {new DummyIFormattable()}, {dateTime:tt}, {dateTime,5:tt}"); + (await action.Should().ThrowAsync()).And.Message.Should().Be($"Assert.AreSame failed. User-provided message. DummyClassTrackingToStringCalls, DummyClassTrackingToStringCalls, Hello, DummyIFormattable.ToString(), {string.Format(null, "{0:tt}", dateTime)}, {string.Format(null, "{0,5:tt}", dateTime)}"); + o.WasToStringCalled.Should().BeTrue(); } public void AreSame_MessageArgs_PassSameObject_ShouldPass() @@ -53,8 +55,8 @@ public void AreSame_MessageArgs_PassSameObject_ShouldPass() public void AreSame_MessageArgs_PassDifferentObject_ShouldFail() { - Exception ex = VerifyThrows(() => Assert.AreSame(new object(), new object(), "User-provided message: System.Object type: {0}", new object().GetType())); - Verify(ex.Message == "Assert.AreSame failed. User-provided message: System.Object type: System.Object"); + Action action = () => Assert.AreSame(new object(), new object(), "User-provided message: System.Object type: {0}", new object().GetType()); + action.Should().Throw().And.Message.Should().Be("Assert.AreSame failed. User-provided message: System.Object type: System.Object"); } public void AreNotSame_PassDifferentObject_ShouldPass() @@ -62,33 +64,33 @@ public void AreNotSame_PassDifferentObject_ShouldPass() public void AreSame_BothAreValueTypes_ShouldFailWithSpecializedMessage() { - Exception ex = VerifyThrows(() => Assert.AreSame(1, 1)); - Verify(ex.Message == "Assert.AreSame failed. Do not pass value types to AreSame(). Values converted to Object will never be the same. Consider using AreEqual(). "); + Action action = () => Assert.AreSame(1, 1); + action.Should().Throw().And.Message.Should().Be("Assert.AreSame failed. Do not pass value types to AreSame(). Values converted to Object will never be the same. Consider using AreEqual(). "); } public void AreSame_StringMessage_BothAreValueTypes_ShouldFailWithSpecializedMessage() { - Exception ex = VerifyThrows(() => Assert.AreSame(1, 1, "User-provided message")); - Verify(ex.Message == "Assert.AreSame failed. Do not pass value types to AreSame(). Values converted to Object will never be the same. Consider using AreEqual(). User-provided message"); + Action action = () => Assert.AreSame(1, 1, "User-provided message"); + action.Should().Throw().And.Message.Should().Be("Assert.AreSame failed. Do not pass value types to AreSame(). Values converted to Object will never be the same. Consider using AreEqual(). User-provided message"); } public void AreSame_InterpolatedString_BothAreValueTypes_ShouldFailWithSpecializedMessage() { - Exception ex = VerifyThrows(() => Assert.AreSame(1, 1, $"User-provided message {new object().GetType()}")); - Verify(ex.Message == "Assert.AreSame failed. Do not pass value types to AreSame(). Values converted to Object will never be the same. Consider using AreEqual(). User-provided message System.Object"); + Action action = () => Assert.AreSame(1, 1, $"User-provided message {new object().GetType()}"); + action.Should().Throw().And.Message.Should().Be("Assert.AreSame failed. Do not pass value types to AreSame(). Values converted to Object will never be the same. Consider using AreEqual(). User-provided message System.Object"); } public void AreSame_MessageArgs_BothAreValueTypes_ShouldFailWithSpecializedMessage() { - Exception ex = VerifyThrows(() => Assert.AreSame(1, 1, "User-provided message {0}", new object().GetType())); - Verify(ex.Message == "Assert.AreSame failed. Do not pass value types to AreSame(). Values converted to Object will never be the same. Consider using AreEqual(). User-provided message System.Object"); + Action action = () => Assert.AreSame(1, 1, "User-provided message {0}", new object().GetType()); + action.Should().Throw().And.Message.Should().Be("Assert.AreSame failed. Do not pass value types to AreSame(). Values converted to Object will never be the same. Consider using AreEqual(). User-provided message System.Object"); } public void AreNotSame_PassSameObject_ShouldFail() { object o = new(); - Exception ex = VerifyThrows(() => Assert.AreNotSame(o, o)); - Verify(ex.Message == "Assert.AreNotSame failed. "); + Action action = () => Assert.AreNotSame(o, o); + action.Should().Throw().And.Message.Should().Be("Assert.AreNotSame failed. "); } public void AreNotSame_StringMessage_PassDifferentObject_ShouldPass() @@ -97,24 +99,24 @@ public void AreNotSame_StringMessage_PassDifferentObject_ShouldPass() public void AreNotSame_StringMessage_PassSameObject_ShouldFail() { object o = new(); - Exception ex = VerifyThrows(() => Assert.AreNotSame(o, o, "User-provided message")); - Verify(ex.Message == "Assert.AreNotSame failed. User-provided message"); + Action action = () => Assert.AreNotSame(o, o, "User-provided message"); + action.Should().Throw().And.Message.Should().Be("Assert.AreNotSame failed. User-provided message"); } public void AreNotSame_InterpolatedString_PassDifferentObject_ShouldPass() { DummyClassTrackingToStringCalls o = new(); Assert.AreNotSame(new object(), new object(), $"User-provided message: {o}"); - Verify(!o.WasToStringCalled); + o.WasToStringCalled.Should().BeFalse(); } public async Task AreNotSame_InterpolatedString_PassSameObject_ShouldFail() { DummyClassTrackingToStringCalls o = new(); DateTime dateTime = DateTime.Now; - Exception ex = await VerifyThrowsAsync(async () => Assert.AreNotSame(o, o, $"User-provided message. {o}, {o,35}, {await GetHelloStringAsync()}, {new DummyIFormattable()}, {dateTime:tt}, {dateTime,5:tt}")); - Verify(ex.Message == $"Assert.AreNotSame failed. User-provided message. DummyClassTrackingToStringCalls, DummyClassTrackingToStringCalls, Hello, DummyIFormattable.ToString(), {string.Format(null, "{0:tt}", dateTime)}, {string.Format(null, "{0,5:tt}", dateTime)}"); - Verify(o.WasToStringCalled); + Func action = async () => Assert.AreNotSame(o, o, $"User-provided message. {o}, {o,35}, {await GetHelloStringAsync()}, {new DummyIFormattable()}, {dateTime:tt}, {dateTime,5:tt}"); + (await action.Should().ThrowAsync()).And.Message.Should().Be($"Assert.AreNotSame failed. User-provided message. DummyClassTrackingToStringCalls, DummyClassTrackingToStringCalls, Hello, DummyIFormattable.ToString(), {string.Format(null, "{0:tt}", dateTime)}, {string.Format(null, "{0,5:tt}", dateTime)}"); + o.WasToStringCalled.Should().BeTrue(); } public void AreNotSame_MessageArgs_PassDifferentObject_ShouldPass() @@ -123,7 +125,7 @@ public void AreNotSame_MessageArgs_PassDifferentObject_ShouldPass() public void AreNotSame_MessageArgs_PassSameObject_ShouldFail() { object o = new(); - Exception ex = VerifyThrows(() => Assert.AreNotSame(o, o, "User-provided message: System.Object type: {0}", new object().GetType())); - Verify(ex.Message == "Assert.AreNotSame failed. User-provided message: System.Object type: System.Object"); + Action action = () => Assert.AreNotSame(o, o, "User-provided message: System.Object type: {0}", new object().GetType()); + action.Should().Throw().And.Message.Should().Be("Assert.AreNotSame failed. User-provided message: System.Object type: System.Object"); } } diff --git a/test/UnitTests/TestFramework.UnitTests/Assertions/AssertTests.Contains.cs b/test/UnitTests/TestFramework.UnitTests/Assertions/AssertTests.Contains.cs index 10c11bb8b0..ffb1ce1b24 100644 --- a/test/UnitTests/TestFramework.UnitTests/Assertions/AssertTests.Contains.cs +++ b/test/UnitTests/TestFramework.UnitTests/Assertions/AssertTests.Contains.cs @@ -1,7 +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 FluentAssertions; +using AwesomeAssertions; using TestFramework.ForTestingMSTest; @@ -19,14 +19,13 @@ public partial class AssertTests : TestContainer /// /// The type parameter. /// The handler instance. - /// The assertion name. /// The exception message thrown by ComputeAssertion. - private static string GetComputeAssertionExceptionMessage(Assert.AssertSingleInterpolatedStringHandler handler, string assertionName) + private static string GetComputeAssertionExceptionMessage(Assert.AssertSingleInterpolatedStringHandler handler) { try { // This call is expected to throw when _builder is not null. - _ = handler.ComputeAssertion(assertionName); + _ = handler.ComputeAssertion(); } catch (Exception ex) { @@ -52,7 +51,7 @@ public void ComputeAssertion_WhenCollectionHasSingleElement_ReturnsElement() shouldAppend.Should().BeFalse(); // Act - int result = handler.ComputeAssertion("ContainsSingle"); + int result = handler.ComputeAssertion(); // Assert result.Should().Be(singleItem); @@ -69,7 +68,7 @@ public void ComputeAssertion_WhenCollectionDoesNotHaveSingleElement_ThrowsExcept shouldAppend.Should().BeTrue(); // Act - string exMsg = GetComputeAssertionExceptionMessage(handler, "ContainsSingle"); + string exMsg = GetComputeAssertionExceptionMessage(handler); // Assert: verify that the exception message contains expected parts. exMsg.Should().Contain("ContainsSingle"); @@ -93,7 +92,7 @@ public void AppendLiteral_WhenCalled_AppendsLiteralText() handler.AppendLiteral(literal); // Act - string exMsg = GetComputeAssertionExceptionMessage(handler, "ContainsSingle"); + string exMsg = GetComputeAssertionExceptionMessage(handler); // Assert: the exception message should contain the literal appended. exMsg.Should().Contain(literal); @@ -112,7 +111,7 @@ public void AppendFormatted_GenericValue_WithoutFormat_AppendsValue() handler.AppendFormatted(value); // Act - string exMsg = GetComputeAssertionExceptionMessage(handler, "ContainsSingle"); + string exMsg = GetComputeAssertionExceptionMessage(handler); // Assert exMsg.Should().Contain(value); @@ -132,7 +131,7 @@ public void AppendFormatted_GenericValue_WithFormat_AppendsFormattedValue() handler.AppendFormatted(value, format); // Act - string exMsg = GetComputeAssertionExceptionMessage(handler, "ContainsSingle"); + string exMsg = GetComputeAssertionExceptionMessage(handler); // Assert: Check if the value was formatted accordingly ("00123") exMsg.Should().Contain("00123"); @@ -152,7 +151,7 @@ public void AppendFormatted_GenericValue_WithAlignment_AppendsAlignedValue() handler.AppendFormatted(value, alignment); // Act - string exMsg = GetComputeAssertionExceptionMessage(handler, "ContainsSingle"); + string exMsg = GetComputeAssertionExceptionMessage(handler); // Assert: alignment is applied via StringBuilder.AppendFormat so result should contain formatted spacing. exMsg.Should().Contain("3.14"); @@ -173,7 +172,7 @@ public void AppendFormatted_GenericValue_WithAlignmentAndFormat_AppendsFormatted handler.AppendFormatted(value, alignment, format); // Act - string exMsg = GetComputeAssertionExceptionMessage(handler, "ContainsSingle"); + string exMsg = GetComputeAssertionExceptionMessage(handler); // Assert: formatted year "2023" should appear. exMsg.Should().Contain("2023"); @@ -192,7 +191,7 @@ public void AppendFormatted_StringValue_WithoutAdditionalParameters_AppendsStrin handler.AppendFormatted(value); // Act - string exMsg = GetComputeAssertionExceptionMessage(handler, "ContainsSingle"); + string exMsg = GetComputeAssertionExceptionMessage(handler); // Assert exMsg.Should().Contain(value); @@ -213,7 +212,7 @@ public void AppendFormatted_StringValue_WithAlignmentAndFormat_AppendsFormattedS handler.AppendFormatted(value, alignment, format); // Act - string exMsg = GetComputeAssertionExceptionMessage(handler, "ContainsSingle"); + string exMsg = GetComputeAssertionExceptionMessage(handler); // Assert exMsg.Should().Contain(value); @@ -234,7 +233,7 @@ public void AppendFormatted_ObjectValue_WithAlignmentAndFormat_AppendsFormattedO handler.AppendFormatted(value, alignment, format); // Act - string exMsg = GetComputeAssertionExceptionMessage(handler, "ContainsSingle"); + string exMsg = GetComputeAssertionExceptionMessage(handler); // Assert: Formatted value should appear (e.g. "099"). exMsg.Should().Contain("099"); @@ -254,7 +253,7 @@ public void AppendFormatted_ReadOnlySpan_AppendsSpanValue() handler.AppendFormatted(spanValue); // Act - string exMsg = GetComputeAssertionExceptionMessage(handler, "ContainsSingle"); + string exMsg = GetComputeAssertionExceptionMessage(handler); // Assert exMsg.Should().Contain("SpanText"); @@ -309,7 +308,7 @@ public void ContainsSingle_InterpolatedHandler_WithMultipleElements_ThrowsExcept Action action = () => Assert.ContainsSingle(collection, ref handler); // Assert - action.Should().Throw().WithMessage("*1*"); + action.Should().Throw().WithMessage("Assert.ContainsSingle failed. Expected collection to contain exactly one element but found 3 element(s). "); } /// @@ -363,6 +362,84 @@ public void Contains_ValueExpected_ItemDoesNotExist_ThrowsException() action.Should().Throw().WithMessage("*20*"); } + /// + /// Tests the Contains method (value overload) when the expected item is present. + /// + public void Contains_InNonGenericCollection_ValueExpected_ItemDoesNotExist_ThrowsException() + { + // Arrange + var collection = new ArrayList { 5, 10, "a" }; + object expected = 20; + + // Act + Action action = () => Assert.Contains(expected, collection, $"Item {expected} not found"); + + // Assert + action.Should().Throw().WithMessage($"*Item {expected} not found*"); + } + + /// + /// Tests the Contains method (value overload) when the expected item is present. + /// + public void Contains_InNonGenericCollection_ValueExpected_ItemExists_DoesNotThrow() + { + // Arrange + var collection = new ArrayList { 5, 10, "a" }; + + // Act + Action action = () => Assert.Contains("a", collection, "No failure expected"); + + // Assert + action.Should().NotThrow(); + } + + /// + /// Tests the Contains method (value overload) when the expected item is present. + /// + public void Contains_InNonGenericCollection_ValueExpected_ItemExists_DoesNotThrowException() + { + // Arrange + var collection = new ArrayList { 5, 10, "a" }; + + // Act + Action action = () => Assert.Contains(5, collection); + + // Assert + action.Should().NotThrow(); + } + + /// + /// Tests the Contains method (value overload) when the expected item is not present. + /// + public void Contains_InNonGenericCollection_NullableValueExpected_ItemDoesNotExist_ThrowsException() + { + // Arrange + var collection = new ArrayList { 5, 10, "a" }; + object? expected = null; + + // Act + Action action = () => Assert.Contains(expected, collection, $"Item {expected} not found"); + + // Assert + action.Should().Throw().WithMessage($"*Item {expected} not found*"); + } + + /// + /// Tests the Contains method (value overload) when the expected item is present. + /// + public void Contains_InNonGenericCollection_NullableValueExpected_ItemExists_DoesNotThrow() + { + // Arrange + var collection = new ArrayList { null, 10, "a" }; + object? expected = null; + + // Act + Action action = () => Assert.Contains(expected, collection, "No failure expected"); + + // Assert + action.Should().NotThrow(); + } + /// /// Tests the Contains method with a comparer when the expected item is present. /// @@ -411,6 +488,21 @@ public void Contains_Predicate_ItemMatches_DoesNotThrow() action.Should().NotThrow(); } + /// + /// Tests the Contains method that accepts a predicate when an element satisfies the condition. + /// + public void Contains_InNonGenericCollection_Predicate_ItemMatches_DoesNotThrow() + { + // Arrange + var collection = new ArrayList { 2, 4, 6, "a" }; + + // Act + Action action = () => Assert.Contains(IsEven, collection, "Even number exists"); + + // Assert + action.Should().NotThrow(); + } + /// /// Tests the Contains method that accepts a predicate when no element satisfies the condition. /// Expects an exception. @@ -427,6 +519,22 @@ public void Contains_Predicate_NoItemMatches_ThrowsException() action.Should().Throw().WithMessage("*even*"); } + /// + /// Tests the Contains method that accepts a predicate when no element satisfies the condition. + /// Expects an exception. + /// + public void Contains_InNonGenericCollection_Predicate_NoItemMatches_ThrowsException() + { + // Arrange + var collection = new ArrayList { 1, 3, 5, "a" }; + + // Act + Action action = () => Assert.Contains(IsEven, collection, "No even number found"); + + // Assert + action.Should().Throw().WithMessage("*even*"); + } + /// /// Tests the string Contains overload when the substring is contained in the value. /// @@ -495,6 +603,37 @@ public void DoesNotContain_ValueExpected_ItemPresent_ThrowsException() action.Should().Throw().WithMessage("*10*"); } + /// + /// Tests the DoesNotContain method (value overload) when the expected item is not present. + /// + public void DoesNotContain_InNonGenericCollection_ValueExpected_ItemNotPresent_DoesNotThrow() + { + // Arrange + var collection = new ArrayList { 5, 10, 15 }; + + // Act + Action action = () => Assert.DoesNotContain(20, collection, "No failure expected"); + + // Assert + action.Should().NotThrow(); + } + + /// + /// Tests the DoesNotContain method (value overload) when the expected item is present. + /// Expects an exception. + /// + public void DoesNotContain_InNonGenericCollection_ValueExpected_ItemPresent_ThrowsException() + { + // Arrange + var collection = new ArrayList { 5, 10, 15, "a" }; + + // Act + Action action = () => Assert.DoesNotContain(10, collection, "Assert.DoesNotContain failed. Expected collection to not contain the specified item. Item {0} should not be found"); + + // Assert + action.Should().Throw().WithMessage("*Assert.DoesNotContain failed. Expected collection to not contain the specified item. Item {0} should not be found*"); + } + /// /// Tests the DoesNotContain method with a comparer when the item is not present. /// @@ -511,6 +650,22 @@ public void DoesNotContain_WithComparer_ItemNotPresent_DoesNotThrow() action.Should().NotThrow(); } + /// + /// Tests the DoesNotContain method with a comparer when the item is not present. + /// + public void DoesNotContain_InNonGenericCollection_WithComparer_ItemNotPresent_DoesNotThrow() + { + // Arrange + var collection = new ArrayList { "apple", "banana", 1 }; + IEqualityComparer comparer = StringComparer.OrdinalIgnoreCase; + + // Act + Action action = () => Assert.DoesNotContain("cherry", collection, comparer, "No cherry found"); + + // Assert + action.Should().NotThrow(); + } + /// /// Tests the DoesNotContain method with a comparer when the item is present. /// Expects an exception. @@ -528,6 +683,23 @@ public void DoesNotContain_WithComparer_ItemPresent_ThrowsException() action.Should().Throw().WithMessage("*APPLE*"); } + /// + /// Tests the DoesNotContain method with a comparer when the item is present. + /// Expects an exception. + /// + public void DoesNotContain_InNonGenericCollection_WithComparer_ItemPresent_ThrowsException() + { + // Arrange + var collection = new ArrayList { "apple", "banana", 1 }; + IEqualityComparer comparer = StringComparer.OrdinalIgnoreCase; + + // Act + Action action = () => Assert.DoesNotContain("APPLE", collection, comparer, "APPLE"); + + // Assert + action.Should().Throw().WithMessage("*APPLE*"); + } + /// /// Tests the DoesNotContain method that accepts a predicate when no element satisfies the predicate. /// @@ -543,6 +715,21 @@ public void DoesNotContain_Predicate_NoItemMatches_DoesNotThrow() action.Should().NotThrow(); } + /// + /// Tests the DoesNotContain method that accepts a predicate when no element satisfies the predicate. + /// + public void DoesNotContain_InNonGenericCollection_Predicate_NoItemMatches_DoesNotThrow() + { + // Arrange + var collection = new ArrayList { 1, 3, 5, "a" }; + + // Act + Action action = () => Assert.DoesNotContain(IsEven, collection, "All items are odd"); + + // Assert + action.Should().NotThrow(); + } + /// /// Tests the DoesNotContain method that accepts a predicate when at least one element satisfies the predicate. /// Expects an exception. @@ -559,6 +746,22 @@ public void DoesNotContain_Predicate_AtLeastOneItemMatches_ThrowsException() action.Should().Throw().WithMessage("*even*"); } + /// + /// Tests the DoesNotContain method that accepts a predicate when at least one element satisfies the predicate. + /// Expects an exception. + /// + public void DoesNotContain_InNonGenericCollection_Predicate_AtLeastOneItemMatches_ThrowsException() + { + // Arrange + var collection = new ArrayList { 2, 3, 5, "a" }; + + // Act + Action action = () => Assert.DoesNotContain(IsEven, collection, "An even number exists"); + + // Assert + action.Should().Throw().WithMessage("*even*"); + } + /// /// Tests the string DoesNotContain overload when the substring is not contained in the value. /// @@ -592,7 +795,362 @@ public void DoesNotContain_StringVersion_SubstringPresent_ThrowsException() action.Should().Throw().WithMessage("*brown*"); } + /// + /// Tests the string DoesNotContain overload with message and parameters when substring is not present. + /// This test ensures the method overload works correctly and prevents regression of stackoverflow bug. + /// + public void DoesNotContain_StringWithMessageAndParameters_SubstringNotPresent_DoesNotThrow() + { + // Arrange + string value = "The quick brown fox"; + string substring = "lazy"; + + // Act + Action action = () => Assert.DoesNotContain(substring, value, "Custom message: {0}", "test parameter"); + + // Assert + action.Should().NotThrow(); + } + + /// + /// Tests the string DoesNotContain overload with message and parameters when substring is present. + /// This test ensures the method overload works correctly and prevents regression of stackoverflow bug. + /// Expects an exception. + /// + public void DoesNotContain_StringWithMessageAndParameters_SubstringPresent_ThrowsException() + { + // Arrange + string value = "The quick brown fox"; + string substring = "brown"; + + // Act + Action action = () => Assert.DoesNotContain(substring, value, "Found unexpected substring: {0}", substring); + + // Assert + action.Should().Throw().WithMessage("*Found unexpected substring: brown*"); + } + + /// + /// Tests the string DoesNotContain overload with StringComparison and message when substring is not present. + /// This test ensures the method overload works correctly and prevents regression of stackoverflow bug. + /// + public void DoesNotContain_StringWithComparisonAndMessage_SubstringNotPresent_DoesNotThrow() + { + // Arrange + string value = "The quick brown fox"; + string substring = "LAZY"; + + // Act + Action action = () => Assert.DoesNotContain(substring, value, StringComparison.OrdinalIgnoreCase, "Should not contain lazy"); + + // Assert + action.Should().NotThrow(); + } + + /// + /// Tests the string DoesNotContain overload with StringComparison and message when substring is present. + /// This test ensures the method overload works correctly and prevents regression of stackoverflow bug. + /// Expects an exception. + /// + public void DoesNotContain_StringWithComparisonAndMessage_SubstringPresent_ThrowsException() + { + // Arrange + string value = "The quick brown fox"; + string substring = "BROWN"; + + // Act + Action action = () => Assert.DoesNotContain(substring, value, StringComparison.OrdinalIgnoreCase, "Found unexpected substring"); + + // Assert + action.Should().Throw().WithMessage("*Found unexpected substring*"); + } + + /// + /// Tests the simplest string DoesNotContain overload when substring is not present. + /// + public void DoesNotContain_StringSimpleOverload_SubstringNotPresent_DoesNotThrow() + { + // Arrange + string value = "The quick brown fox"; + string substring = "lazy"; + + // Act + Action action = () => Assert.DoesNotContain(substring, value); + + // Assert + action.Should().NotThrow(); + } + + /// + /// Tests the simplest string DoesNotContain overload when substring is present. + /// Expects an exception. + /// + public void DoesNotContain_StringSimpleOverload_SubstringPresent_ThrowsException() + { + // Arrange + string value = "The quick brown fox"; + string substring = "brown"; + + // Act + Action action = () => Assert.DoesNotContain(substring, value); + + // Assert + action.Should().Throw().WithMessage("*brown*"); + } + + /// + /// Tests the string DoesNotContain overload with message only when substring is not present. + /// + public void DoesNotContain_StringWithMessageOnly_SubstringNotPresent_DoesNotThrow() + { + // Arrange + string value = "The quick brown fox"; + string substring = "lazy"; + + // Act + Action action = () => Assert.DoesNotContain(substring, value, "Should not contain lazy"); + + // Assert + action.Should().NotThrow(); + } + + /// + /// Tests the string DoesNotContain overload with message only when substring is present. + /// Expects an exception. + /// + public void DoesNotContain_StringWithMessageOnly_SubstringPresent_ThrowsException() + { + // Arrange + string value = "The quick brown fox"; + string substring = "brown"; + + // Act + Action action = () => Assert.DoesNotContain(substring, value, "Found unexpected substring"); + + // Assert + action.Should().Throw().WithMessage("*Found unexpected substring*"); + } + private static bool IsEven(int x) => x % 2 == 0; + private static bool IsEven(object? x) => x is int i && i % 2 == 0; + + #endregion + + #region ContainsSingle with Predicate Tests + + /// + /// Tests the ContainsSingle method with predicate when exactly one element matches. + /// + public void ContainsSinglePredicate_NoMessage_OneItemMatches_ReturnsElement() + { + // Arrange + var collection = new List { 1, 2, 3, 4, 5 }; + + // Act + int result = Assert.ContainsSingle(x => x == 3, collection); + + // Assert + result.Should().Be(3); + } + + /// + /// Tests the ContainsSingle method with predicate and message when exactly one element matches. + /// + public void ContainsSinglePredicate_WithMessage_OneItemMatches_ReturnsElement() + { + // Arrange + var collection = new List { "apple", "banana", "cherry" }; + + // Act +#pragma warning disable CA1865 // Use char overload - not netfx + string result = Assert.ContainsSingle(x => x.StartsWith("b", StringComparison.Ordinal), collection, "Expected one item starting with 'b'"); +#pragma warning restore CA1865 // Use char overload + + // Assert + result.Should().Be("banana"); + } + + /// + /// Tests the ContainsSingle method with predicate when no elements match. + /// Expects an exception. + /// + public void ContainsSinglePredicate_NoItemMatches_ThrowsException() + { + // Arrange + var collection = new List { 1, 3, 5 }; + + // Act + Action action = () => Assert.ContainsSingle(x => x % 2 == 0, collection); + + // Assert + action.Should().Throw().WithMessage("*Expected exactly one item to match the predicate but found 0 item(s)*"); + } + + /// + /// Tests the ContainsSingle method with predicate when multiple elements match. + /// Expects an exception. + /// + public void ContainsSinglePredicate_MultipleItemsMatch_ThrowsException() + { + // Arrange + var collection = new List { 2, 4, 6, 8 }; + + // Act + Action action = () => Assert.ContainsSingle(x => x % 2 == 0, collection); + + // Assert + action.Should().Throw().WithMessage("*Expected exactly one item to match the predicate but found 4 item(s)*"); + } + + /// + /// Tests the ContainsSingle method with predicate and formatted message when no elements match. + /// Expects an exception with the custom message. + /// + public void ContainsSinglePredicate_WithMessage_NoItemMatches_ThrowsException() + { + // Arrange + var collection = new List { 1, 3, 5 }; + + // Act + Action action = () => Assert.ContainsSingle(x => x % 2 == 0, collection, $"No even numbers found in collection with {collection.Count} items"); + + // Assert + action.Should().Throw().WithMessage("*No even numbers found in collection with 3 items*"); + } + + /// + /// Tests the ContainsSingle method with predicate and formatted message when multiple elements match. + /// Expects an exception with the custom message. + /// + public void ContainsSinglePredicate_WithMessage_MultipleItemsMatch_ThrowsException() + { + // Arrange + var collection = new List { 2, 4, 6 }; + + // Act + Action action = () => Assert.ContainsSingle(x => x % 2 == 0, collection, $"Too many even numbers found: {collection.Count}"); + + // Assert + action.Should().Throw().WithMessage("*Too many even numbers found: 3*"); + } + + /// + /// Tests the ContainsSingle method with predicate using complex objects. + /// + public void ContainsSinglePredicate_ComplexObjects_OneItemMatches_ReturnsElement() + { + // Arrange + var items = new List + { + new("Alice", 25), + new("Bob", 30), + new("Charlie", 35), + }; + + // Act + Person result = Assert.ContainsSingle(p => p.Age == 30, items); + + // Assert + result.Name.Should().Be("Bob"); + result.Age.Should().Be(30); + } + + /// + /// Tests the ContainsSingle method with predicate using null values. + /// + public void ContainsSinglePredicate_WithNullValues_OneItemMatches_ReturnsElement() + { + // Arrange + var collection = new List { "apple", null, "banana" }; + + // Act + string? result = Assert.ContainsSingle(x => x == null, collection); + + // Assert + result.Should().BeNull(); + } + + #region New Error Message Tests + + /// + /// Tests that Contains (item) failure shows specific error message. + /// + public void Contains_ItemNotFound_ShowsSpecificErrorMessage() + { + // Arrange + var collection = new List { 1, 2, 3 }; + + // Act + Action action = () => Assert.Contains(5, collection); + + // Assert + action.Should().Throw().WithMessage("*Expected collection to contain the specified item*"); + } + + /// + /// Tests that Contains (item) failure shows specific error message. + /// + public void Contains_InNonGenericCollection_ItemNotFound_ShowsSpecificErrorMessage() + { + // Arrange + var collection = new ArrayList { 1, 2, 3 }; + + // Act + Action action = () => Assert.Contains(5, collection); + + // Assert + action.Should().Throw().WithMessage("*Expected collection to contain the specified item*"); + } + + /// + /// Tests that Contains (predicate) failure shows specific error message. + /// + public void Contains_PredicateNotMatched_ShowsSpecificErrorMessage() + { + // Arrange + var collection = new List { 1, 3, 5 }; + + // Act + Action action = () => Assert.Contains(x => x % 2 == 0, collection); + + // Assert + action.Should().Throw().WithMessage("*Expected at least one item to match the predicate*"); + } + + /// + /// Tests that DoesNotContain (item) failure shows specific error message. + /// + public void DoesNotContain_ItemFound_ShowsSpecificErrorMessage() + { + // Arrange + var collection = new List { 1, 2, 3 }; + + // Act + Action action = () => Assert.DoesNotContain(2, collection); + + // Assert + action.Should().Throw().WithMessage("*Expected collection to not contain the specified item*"); + } + + /// + /// Tests that DoesNotContain (predicate) failure shows specific error message. + /// + public void DoesNotContain_PredicateMatched_ShowsSpecificErrorMessage() + { + // Arrange + var collection = new List { 1, 2, 3 }; + + // Act + Action action = () => Assert.DoesNotContain(x => x % 2 == 0, collection); + + // Assert + action.Should().Throw().WithMessage("*Expected no items to match the predicate*"); + } + + #endregion + + private record Person(string Name, int Age); + #endregion } diff --git a/test/UnitTests/TestFramework.UnitTests/Assertions/AssertTests.IComparableTests.cs b/test/UnitTests/TestFramework.UnitTests/Assertions/AssertTests.IComparableTests.cs new file mode 100644 index 0000000000..4b17cb9ae4 --- /dev/null +++ b/test/UnitTests/TestFramework.UnitTests/Assertions/AssertTests.IComparableTests.cs @@ -0,0 +1,317 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using AwesomeAssertions; + +using TestFramework.ForTestingMSTest; + +namespace Microsoft.VisualStudio.TestPlatform.TestFramework.UnitTests; + +public partial class AssertTests : TestContainer +{ + #region IsGreaterThan tests + + public void IsGreaterThanShouldNotThrowWhenActualIsGreater() => + Assert.IsGreaterThan(5, 10); + + public void IsGreaterThanShouldWorkWithReferenceTypes() => + Assert.IsGreaterThan("a", "b"); + + public void IsGreaterThanShouldThrowWhenActualIsNotGreater() + { + // Act + Action action = () => Assert.IsGreaterThan(10, 5); + + // Assert + action.Should().Throw(); + } + + public void IsGreaterThanShouldThrowWhenBothAreEqual() + { + // Act + Action action = () => Assert.IsGreaterThan(5, 5); + + // Assert + action.Should().Throw(); + } + + public void IsGreaterThanShouldThrowWithMessage() + { + // Act + Action action = () => Assert.IsGreaterThan(10, 5, "A Message"); + + // Assert + action.Should().Throw() + .WithMessage("Assert.IsGreaterThan failed. Actual value <5> is not greater than expected value <10>. A Message"); + } + + public void IsGreaterThanShouldWorkWithDoubles() => + Assert.IsGreaterThan(5.0, 5.5); + + public void IsGreaterThanShouldThrowWithDoubles() + { + // Act + Action action = () => Assert.IsGreaterThan(5.5, 5.0); + + // Assert + action.Should().Throw(); + } + + #endregion + + #region IsGreaterThanOrEqualTo tests + + public void IsGreaterThanOrEqualToShouldNotThrowWhenActualIsGreater() => + Assert.IsGreaterThanOrEqualTo(5, 10); + + public void IsGreaterThanOrEqualToShouldWorkWithReferenceTypes() => + Assert.IsGreaterThanOrEqualTo("a", "b"); + + public void IsGreaterThanOrEqualToShouldNotThrowWhenBothAreEqual() => + Assert.IsGreaterThanOrEqualTo(5, 5); + + public void IsGreaterThanOrEqualToShouldThrowWhenActualIsLess() + { + // Act + Action action = () => Assert.IsGreaterThanOrEqualTo(10, 5); + + // Assert + action.Should().Throw(); + } + + public void IsGreaterThanOrEqualToShouldThrowWithMessage() + { + // Act + Action action = () => Assert.IsGreaterThanOrEqualTo(10, 5, "A Message"); + + // Assert + action.Should().Throw() + .WithMessage("Assert.IsGreaterThanOrEqualTo failed. Actual value <5> is not greater than or equal to expected value <10>. A Message"); + } + + public void IsGreaterThanOrEqualToShouldWorkWithDoubles() => + Assert.IsGreaterThanOrEqualTo(5.0, 5.5); + + public void IsGreaterThanOrEqualToShouldWorkWithEqualDoubles() => + Assert.IsGreaterThanOrEqualTo(5.5, 5.5); + + #endregion + + #region IsLessThan tests + + public void IsLessThanShouldNotThrowWhenActualIsLess() => + Assert.IsLessThan(10, 5); + + public void IsLessThanShouldWorkWithReferenceTypes() => + Assert.IsLessThan("b", "a"); + + public void IsLessThanShouldThrowWhenActualIsNotLess() + { + // Act + Action action = () => Assert.IsLessThan(5, 10); + + // Assert + action.Should().Throw(); + } + + public void IsLessThanShouldThrowWhenBothAreEqual() + { + // Act + Action action = () => Assert.IsLessThan(5, 5); + + // Assert + action.Should().Throw(); + } + + public void IsLessThanShouldThrowWithMessage() + { + // Act + Action action = () => Assert.IsLessThan(5, 10, "A Message"); + + // Assert + action.Should().Throw() + .WithMessage("Assert.IsLessThan failed. Actual value <10> is not less than expected value <5>. A Message"); + } + + public void IsLessThanShouldWorkWithDoubles() => + Assert.IsLessThan(5.5, 5.0); + + public void IsLessThanShouldThrowWithDoubles() + { + // Act + Action action = () => Assert.IsLessThan(5.0, 5.5); + + // Assert + action.Should().Throw(); + } + + #endregion + + #region IsLessThanOrEqualTo tests + + public void IsLessThanOrEqualToShouldNotThrowWhenActualIsLess() => + Assert.IsLessThanOrEqualTo(10, 5); + + public void IsLessThanOrEqualToShouldWorkWithReferenceTypes() => + Assert.IsLessThanOrEqualTo("b", "a"); + + public void IsLessThanOrEqualToShouldNotThrowWhenBothAreEqual() => + Assert.IsLessThanOrEqualTo(5, 5); + + public void IsLessThanOrEqualToShouldThrowWhenActualIsGreater() + { + // Act + Action action = () => Assert.IsLessThanOrEqualTo(5, 10); + + // Assert + action.Should().Throw(); + } + + public void IsLessThanOrEqualToShouldThrowWithMessage() + { + // Act + Action action = () => Assert.IsLessThanOrEqualTo(5, 10, "A Message"); + + // Assert + action.Should().Throw() + .WithMessage("Assert.IsLessThanOrEqualTo failed. Actual value <10> is not less than or equal to expected value <5>. A Message"); + } + + public void IsLessThanOrEqualToShouldWorkWithDoubles() => + Assert.IsLessThanOrEqualTo(5.5, 5.0); + + public void IsLessThanOrEqualToShouldWorkWithEqualDoubles() => + Assert.IsLessThanOrEqualTo(5.5, 5.5); + + #endregion + + #region IsPositive tests + + public void IsPositiveShouldNotThrowForPositiveNumber() => + Assert.IsPositive(5); + + public void IsPositiveShouldThrowForZero() + { + // Act + Action action = () => Assert.IsPositive(0); + + // Assert + action.Should().Throw(); + } + + public void IsPositiveShouldThrowForNegativeNumber() + { + // Act + Action action = () => Assert.IsPositive(-5); + + // Assert + action.Should().Throw(); + } + + public void IsPositiveShouldThrowForNaN() + { + // Act + Action action = () => Assert.IsPositive(float.NaN); + + // Assert + action.Should().Throw(); + } + + public void IsPositiveShouldThrowForDoubleNaN() + { + // Act + Action action = () => Assert.IsPositive(double.NaN); + + // Assert + action.Should().Throw(); + } + + public void IsPositiveShouldThrowWithMessage() + { + // Act + Action action = () => Assert.IsPositive(-5, "A Message"); + + // Assert + action.Should().Throw() + .WithMessage("Assert.IsPositive failed. Expected value <-5> to be positive. A Message"); + } + + public void IsPositiveShouldWorkWithDoubles() => + Assert.IsPositive(5.5); + + public void IsPositiveShouldThrowForZeroDouble() + { + // Act + Action action = () => Assert.IsPositive(0.0); + + // Assert + action.Should().Throw(); + } + + #endregion + + #region IsNegative tests + + public void IsNegativeShouldNotThrowForNegativeNumber() => + Assert.IsNegative(-5); + + public void IsNegativeShouldThrowForZero() + { + // Act + Action action = () => Assert.IsNegative(0); + + // Assert + action.Should().Throw(); + } + + public void IsNegativeShouldThrowForPositiveNumber() + { + // Act + Action action = () => Assert.IsNegative(5); + + // Assert + action.Should().Throw(); + } + + public void IsNegativeShouldThrowForNaN() + { + // Act + Action action = () => Assert.IsNegative(float.NaN); + + // Assert + action.Should().Throw(); + } + + public void IsNegativeShouldThrowForDoubleNaN() + { + // Act + Action action = () => Assert.IsNegative(double.NaN); + + // Assert + action.Should().Throw(); + } + + public void IsNegativeShouldThrowWithMessage() + { + // Act + Action action = () => Assert.IsNegative(5, "A Message"); + + // Assert + action.Should().Throw() + .WithMessage("Assert.IsNegative failed. Expected value <5> to be negative. A Message"); + } + + public void IsNegativeShouldWorkWithDoubles() => + Assert.IsNegative(-5.5); + + public void IsNegativeShouldThrowForZeroDouble() + { + // Act + Action action = () => Assert.IsNegative(0.0); + + // Assert + action.Should().Throw(); + } + + #endregion +} diff --git a/test/UnitTests/TestFramework.UnitTests/Assertions/AssertTests.InconclusiveTests.cs b/test/UnitTests/TestFramework.UnitTests/Assertions/AssertTests.InconclusiveTests.cs index ab5402696e..cb58a2ecd2 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. // Licensed under the MIT license. See LICENSE file in the project root for full license information. +using AwesomeAssertions; + namespace Microsoft.VisualStudio.TestPlatform.TestFramework.UnitTests; public partial class AssertTests @@ -8,12 +10,16 @@ public partial class AssertTests // See https://github.com/dotnet/sdk/issues/25373 public void InconclusiveDoesNotThrowWhenMessageContainsInvalidStringFormatCompositeAndNoArgumentsPassed() { - Exception ex = VerifyThrows(() => Assert.Inconclusive("{")); - Verify(ex.Message.Contains("Assert.Inconclusive failed. {")); + Action action = () => Assert.Inconclusive("{"); + action.Should().Throw() + .And.Message.Should().Contain("Assert.Inconclusive 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 InconclusiveThrowsWhenMessageContainsInvalidStringFormatComposite() => - VerifyThrows(() => Assert.Inconclusive("{", "arg")); + public void InconclusiveThrowsWhenMessageContainsInvalidStringFormatComposite() + { + Action action = () => Assert.Inconclusive("{", "arg"); + action.Should().Throw(); + } } diff --git a/test/UnitTests/TestFramework.UnitTests/Assertions/AssertTests.IsInRange.cs b/test/UnitTests/TestFramework.UnitTests/Assertions/AssertTests.IsInRange.cs new file mode 100644 index 0000000000..1b729b9da6 --- /dev/null +++ b/test/UnitTests/TestFramework.UnitTests/Assertions/AssertTests.IsInRange.cs @@ -0,0 +1,338 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using AwesomeAssertions; + +using TestFramework.ForTestingMSTest; + +namespace Microsoft.VisualStudio.TestTools.UnitTesting.UnitTests; + +/// +/// Unit tests for the Assert.IsInRange methods. +/// +public partial class AssertTests : TestContainer +{ + #region IsInRange Tests + + public void IsInRange_WithValueInRange_DoesNotThrow() + { + // Arrange + int minValue = 1; + int maxValue = 10; + int value = 5; + + // Act & Assert + Assert.IsInRange(minValue, maxValue, value); + } + + public void IsInRange_WithValueEqualToMin_DoesNotThrow() + { + // Arrange + int minValue = 1; + int maxValue = 10; + int value = 1; + + // Act & Assert + Assert.IsInRange(minValue, maxValue, value); + } + + public void IsInRange_WithValueEqualToMax_DoesNotThrow() + { + // Arrange + int minValue = 1; + int maxValue = 10; + int value = 10; + + // Act & Assert + Assert.IsInRange(minValue, maxValue, value); + } + + public void IsInRange_WithValueBelowRange_ThrowsAssertFailedException() + { + // Arrange + int minValue = 5; + int maxValue = 10; + int value = 3; + + // Act & Assert + Action action = () => Assert.IsInRange(minValue, maxValue, value); + action.Should().Throw() + .And.Message.Should().Contain("Value '3' is not within the expected range [5, 10]"); + } + + public void IsInRange_WithValueAboveRange_ThrowsAssertFailedException() + { + // Arrange + int minValue = 1; + int maxValue = 5; + int value = 8; + + // Act & Assert + Action action = () => Assert.IsInRange(minValue, maxValue, value); + action.Should().Throw() + .And.Message.Should().Contain("Value '8' is not within the expected range [1, 5]"); + } + + public void IsInRange_WithCustomMessage_IncludesCustomMessage() + { + // Arrange + int minValue = 1; + int maxValue = 5; + int value = 10; + string customMessage = "Custom error message"; + + // Act + Action action = () => Assert.IsInRange(minValue, maxValue, value, customMessage); + + // Assert + action.Should().ThrowExactly() + .And.Message.Should().Contain("Value '10' is not within the expected range [1, 5]") + .And.Contain(customMessage); + } + + public void IsInRange_WithMessage_FormatsMessage() + { + // Arrange + int minValue = 1; + int maxValue = 5; + int value = 10; + string message = "Test with parameter: TestValue"; + + // Act + Action action = () => Assert.IsInRange(minValue, maxValue, value, message); + + // Assert + action.Should().ThrowExactly() + .And.Message.Should().Contain("Value '10' is not within the expected range [1, 5]") + .And.Contain("Test with parameter: TestValue"); + } + + public void IsInRange_WithDoubleValues_WorksCorrectly() + { + // Arrange + double minValue = 1.5; + double maxValue = 5.5; + double valueInRange = 3.0; + double valueOutOfRange = 6.0; + + // Act & Assert + Assert.IsInRange(minValue, maxValue, valueInRange); + Action action = () => Assert.IsInRange(minValue, maxValue, valueOutOfRange); + action.Should().Throw() + .And.Message.Should().Contain("Value '6' is not within the expected range [1.5, 5.5]"); + } + + public void IsInRange_WithDateTimeValues_WorksCorrectly() + { + // Arrange + var minValue = new DateTime(2023, 1, 1); + var maxValue = new DateTime(2023, 12, 31); + var valueInRange = new DateTime(2023, 6, 15); + var valueOutOfRange = new DateTime(2024, 1, 1); + + // Act + Assert.IsInRange(minValue, maxValue, valueInRange); + Action action = () => Assert.IsInRange(minValue, maxValue, valueOutOfRange); + + // Assert + action.Should().ThrowExactly() + .And.Message.Should().Contain("is not within the expected range"); + } + + public void IsInRange_WithCharValues_WorksCorrectly() + { + // Arrange + char minValue = 'A'; + char maxValue = 'Z'; + char valueInRange = 'M'; + char valueOutOfRange = 'a'; + + // Act + Assert.IsInRange(minValue, maxValue, valueInRange); + Action action = () => Assert.IsInRange(minValue, maxValue, valueOutOfRange); + + // Assert + action.Should().ThrowExactly() + .And.Message.Should().Contain("Value 'a' is not within the expected range [A, Z]"); + } + + public void IsInRange_WithNullMessage_DoesNotThrow() + { + // Arrange + int minValue = 1; + int maxValue = 5; + int value = 3; + + // Act & Assert + Assert.IsInRange(minValue, maxValue, value, null!); + } + + public void IsInRange_WithEmptyMessage_DoesNotThrow() + { + // Arrange + int minValue = 1; + int maxValue = 5; + int value = 3; + + // Act & Assert + Assert.IsInRange(minValue, maxValue, value, string.Empty); + } + + public void IsInRange_WithMessage_DoesNotThrow() + { + // Arrange + int minValue = 1; + int maxValue = 5; + int value = 3; + + // Act & Assert + Assert.IsInRange(minValue, maxValue, value, "Test message"); + } + + public void IsInRange_WithAllNegativeValuesInRange_DoesNotThrow() + { + // Arrange + int minValue = -10; + int maxValue = -5; + int value = -7; + + // Act & Assert + Assert.IsInRange(minValue, maxValue, value); + } + + public void IsInRange_WithAllNegativeValuesBelowRange_ThrowsAssertFailedException() + { + // Arrange + int minValue = -10; + int maxValue = -5; + int value = -12; + + // Act & Assert + Action action = () => Assert.IsInRange(minValue, maxValue, value); + + // Assert + action.Should().ThrowExactly() + .And.Message.Should().Contain("Value '-12' is not within the expected range [-10, -5]"); + } + + public void IsInRange_WithAllNegativeValuesAboveRange_ThrowsAssertFailedException() + { + // Arrange + int minValue = -10; + int maxValue = -5; + int value = -3; + + // Act + Action action = () => Assert.IsInRange(minValue, maxValue, value); + + // Assert + action.Should().ThrowExactly() + .And.Message.Should().Contain("Value '-3' is not within the expected range [-10, -5]"); + } + + public void IsInRange_WithRangeSpanningNegativeToPositive_ValueInRange_DoesNotThrow() + { + // Arrange + int minValue = -5; + int maxValue = 5; + int value = 0; + + // Act & Assert + Assert.IsInRange(minValue, maxValue, value); + } + + public void IsInRange_WithRangeSpanningNegativeToPositive_NegativeValueInRange_DoesNotThrow() + { + // Arrange + int minValue = -5; + int maxValue = 5; + int value = -3; + + // Act & Assert + Assert.IsInRange(minValue, maxValue, value); + } + + public void IsInRange_WithRangeSpanningNegativeToPositive_PositiveValueInRange_DoesNotThrow() + { + // Arrange + int minValue = -5; + int maxValue = 5; + int value = 3; + + // Act & Assert + Assert.IsInRange(minValue, maxValue, value); + } + + public void IsInRange_WithRangeSpanningNegativeToPositive_ValueBelowRange_ThrowsAssertFailedException() + { + // Arrange + int minValue = -5; + int maxValue = 5; + int value = -7; + + // Act + Action action = () => Assert.IsInRange(minValue, maxValue, value); + + // Assert + action.Should().ThrowExactly() + .And.Message.Should().Contain("Value '-7' is not within the expected range [-5, 5]"); + } + + public void IsInRange_WithRangeSpanningNegativeToPositive_ValueAboveRange_ThrowsAssertFailedException() + { + // Arrange + int minValue = -5; + int maxValue = 5; + int value = 7; + + // Act + Action action = () => Assert.IsInRange(minValue, maxValue, value); + + // Assert + action.Should().ThrowExactly() + .And.Message.Should().Contain("Value '7' is not within the expected range [-5, 5]"); + } + + public void IsInRange_WithNegativeDoubleValues_WorksCorrectly() + { + // Arrange + double minValue = -10.5; + double maxValue = -2.5; + double valueInRange = -5.0; + + // Act & Assert + Assert.IsInRange(minValue, maxValue, valueInRange); + } + + public void IsInRange_WithMaxValueLessThanMinValue_ThrowsArgumentOutOfRangeException() + { + // Arrange + int minValue = 10; + int maxValue = 5; + int value = 7; + + // Act + Action action = () => Assert.IsInRange(minValue, maxValue, value); + + // Assert + action.Should().ThrowExactly() + .And.Message.Should().Contain("The maximum value must be greater than the minimum value"); + } + + public void IsInRange_WithMaxValueEqualToMinValue_ThrowsArgumentOutOfRangeException() + { + // Arrange + int minValue = 5; + int maxValue = 5; + int value = 5; + + // Act + Action action = () => Assert.IsInRange(minValue, maxValue, value); + + // Assert + action.Should().ThrowExactly() + .And.Message.Should().Contain("The maximum value must be greater than the minimum value"); + } + + #endregion // IsInRange Tests +} diff --git a/test/UnitTests/TestFramework.UnitTests/Assertions/AssertTests.IsInstanceOfTypeTests.cs b/test/UnitTests/TestFramework.UnitTests/Assertions/AssertTests.IsInstanceOfTypeTests.cs index b6517c2b60..8f8c1b5400 100644 --- a/test/UnitTests/TestFramework.UnitTests/Assertions/AssertTests.IsInstanceOfTypeTests.cs +++ b/test/UnitTests/TestFramework.UnitTests/Assertions/AssertTests.IsInstanceOfTypeTests.cs @@ -1,6 +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 AwesomeAssertions; + namespace Microsoft.VisualStudio.TestPlatform.TestFramework.UnitTests; [SuppressMessage("Usage", "CA2263:Prefer generic overload when type is known", Justification = "We want to test also the non-generic API")] @@ -8,20 +10,23 @@ public partial class AssertTests { public void InstanceOfTypeShouldFailWhenValueIsNull() { - Exception ex = VerifyThrows(() => Assert.IsInstanceOfType(null, typeof(AssertTests))); - Verify(ex.Message == "Assert.IsInstanceOfType failed. "); + Action action = () => Assert.IsInstanceOfType(null, typeof(AssertTests)); + action.Should().Throw() + .And.Message.Should().Be("Assert.IsInstanceOfType failed. "); } public void InstanceOfTypeShouldFailWhenTypeIsNull() { - Exception ex = VerifyThrows(() => Assert.IsInstanceOfType(5, null)); - Verify(ex.Message == "Assert.IsInstanceOfType failed. "); + Action action = () => Assert.IsInstanceOfType(5, null); + action.Should().Throw() + .And.Message.Should().Be("Assert.IsInstanceOfType failed. "); } public void InstanceOfTypeShouldFailWhenTypeIsMismatched() { - Exception ex = VerifyThrows(() => Assert.IsInstanceOfType(5, typeof(string))); - Verify(ex.Message == "Assert.IsInstanceOfType failed. Expected type:. Actual type:."); + Action action = () => Assert.IsInstanceOfType(5, typeof(string)); + action.Should().Throw() + .And.Message.Should().Be("Assert.IsInstanceOfType failed. Expected type:. Actual type:."); } public void InstanceOfTypeShouldPassOnSameInstance() => Assert.IsInstanceOfType(5, typeof(int)); @@ -30,20 +35,23 @@ public void InstanceOfTypeShouldFailWhenTypeIsMismatched() public void InstanceOfType_WithStringMessage_ShouldFailWhenValueIsNull() { - Exception ex = VerifyThrows(() => Assert.IsInstanceOfType(null, typeof(AssertTests), "User-provided message")); - Verify(ex.Message == "Assert.IsInstanceOfType failed. User-provided message"); + Action action = () => Assert.IsInstanceOfType(null, typeof(AssertTests), "User-provided message"); + action.Should().Throw() + .And.Message.Should().Be("Assert.IsInstanceOfType failed. User-provided message"); } public void InstanceOfType_WithStringMessage_ShouldFailWhenTypeIsNull() { - Exception ex = VerifyThrows(() => Assert.IsInstanceOfType(5, null, "User-provided message")); - Verify(ex.Message == "Assert.IsInstanceOfType failed. User-provided message"); + Action action = () => Assert.IsInstanceOfType(5, null, "User-provided message"); + action.Should().Throw() + .And.Message.Should().Be("Assert.IsInstanceOfType failed. User-provided message"); } public void InstanceOfType_WithStringMessage_ShouldFailWhenTypeIsMismatched() { - Exception ex = VerifyThrows(() => Assert.IsInstanceOfType(5, typeof(string), "User-provided message")); - Verify(ex.Message == "Assert.IsInstanceOfType failed. User-provided message Expected type:. Actual type:."); + Action action = () => Assert.IsInstanceOfType(5, typeof(string), "User-provided message"); + action.Should().Throw() + .And.Message.Should().Be("Assert.IsInstanceOfType failed. User-provided message Expected type:. Actual type:."); } public void InstanceOfType_WithStringMessage_ShouldPassWhenTypeIsCorrect() @@ -53,38 +61,44 @@ public async Task InstanceOfType_WithInterpolatedString_ShouldFailWhenValueIsNul { DummyClassTrackingToStringCalls o = new(); DateTime dateTime = DateTime.Now; - Exception ex = await VerifyThrowsAsync(async () => Assert.IsInstanceOfType(null, typeof(AssertTests), $"User-provided message. {o}, {o,35}, {await GetHelloStringAsync()}, {new DummyIFormattable()}, {dateTime:tt}, {dateTime,5:tt}")); - Verify(ex.Message == $"Assert.IsInstanceOfType failed. User-provided message. DummyClassTrackingToStringCalls, DummyClassTrackingToStringCalls, Hello, DummyIFormattable.ToString(), {string.Format(null, "{0:tt}", dateTime)}, {string.Format(null, "{0,5:tt}", dateTime)}"); - Verify(o.WasToStringCalled); + Func action = async () => Assert.IsInstanceOfType(null, typeof(AssertTests), $"User-provided message. {o}, {o,35}, {await GetHelloStringAsync()}, {new DummyIFormattable()}, {dateTime:tt}, {dateTime,5:tt}"); + (await action.Should().ThrowAsync()) + .And.Message.Should().Be($"Assert.IsInstanceOfType failed. User-provided message. DummyClassTrackingToStringCalls, DummyClassTrackingToStringCalls, Hello, DummyIFormattable.ToString(), {string.Format(null, "{0:tt}", dateTime)}, {string.Format(null, "{0,5:tt}", dateTime)}"); + o.WasToStringCalled.Should().BeTrue(); } public void InstanceOfType_WithInterpolatedString_ShouldFailWhenTypeIsNull() { DummyClassTrackingToStringCalls o = new(); - Exception ex = VerifyThrows(() => Assert.IsInstanceOfType(5, null, $"User-provided message {o}")); - Verify(ex.Message == "Assert.IsInstanceOfType failed. User-provided message DummyClassTrackingToStringCalls"); - Verify(o.WasToStringCalled); + Action action = () => Assert.IsInstanceOfType(5, null, $"User-provided message {o}"); + action.Should().Throw() + .And.Message.Should().Be("Assert.IsInstanceOfType failed. User-provided message DummyClassTrackingToStringCalls"); + o.WasToStringCalled.Should().BeTrue(); } public void InstanceOfType_WithInterpolatedString_ShouldFailWhenTypeIsMismatched() { DummyClassTrackingToStringCalls o = new(); - Exception ex = VerifyThrows(() => Assert.IsInstanceOfType(5, typeof(string), $"User-provided message {o}")); - Verify(ex.Message == "Assert.IsInstanceOfType failed. User-provided message DummyClassTrackingToStringCalls Expected type:. Actual type:."); - Verify(o.WasToStringCalled); + Action action = () => Assert.IsInstanceOfType(5, typeof(string), $"User-provided message {o}"); + action.Should().Throw() + .And.Message.Should().Be("Assert.IsInstanceOfType failed. User-provided message DummyClassTrackingToStringCalls Expected type:. Actual type:."); + o.WasToStringCalled.Should().BeTrue(); } public void InstanceOfType_WithInterpolatedString_ShouldPassWhenTypeIsCorrect() { DummyClassTrackingToStringCalls o = new(); Assert.IsInstanceOfType(5, typeof(int), $"User-provided message {o}"); - Verify(!o.WasToStringCalled); + o.WasToStringCalled.Should().BeFalse(); } public void InstanceNotOfTypeShouldFailWhenValueIsNull() => Assert.IsNotInstanceOfType(null, typeof(object)); - public void InstanceNotOfTypeShouldFailWhenTypeIsNull() => - VerifyThrows(() => Assert.IsNotInstanceOfType(5, null)); + public void InstanceNotOfTypeShouldFailWhenTypeIsNull() + { + Action action = () => Assert.IsNotInstanceOfType(5, null); + action.Should().Throw(); + } public void InstanceNotOfTypeShouldPassOnWrongInstance() => Assert.IsNotInstanceOfType(5L, typeof(int)); @@ -92,27 +106,31 @@ public void InstanceNotOfTypeShouldFailWhenTypeIsNull() => public void IsInstanceOfTypeUsingGenericType_WhenValueIsNull_Fails() { - Exception ex = VerifyThrows(() => Assert.IsInstanceOfType(null)); - Verify(ex.Message == "Assert.IsInstanceOfType failed. "); + Action action = () => Assert.IsInstanceOfType(null); + action.Should().Throw() + .And.Message.Should().Be("Assert.IsInstanceOfType failed. "); } public void IsInstanceOfTypeUsingGenericType_WhenTypeMismatch_Fails() { - Exception ex = VerifyThrows(() => Assert.IsInstanceOfType(5)); - Verify(ex.Message == "Assert.IsInstanceOfType failed. Expected type:. Actual type:."); + Action action = () => Assert.IsInstanceOfType(5); + action.Should().Throw() + .And.Message.Should().Be("Assert.IsInstanceOfType failed. Expected type:. Actual type:."); } public void IsInstanceOfTypeUsingGenericTypeWithOutParameter_WhenValueIsNull_Fails() { AssertTests? assertTests = null; - VerifyThrows(() => Assert.IsInstanceOfType(null, out assertTests)); - Verify(assertTests is null); + Action action = () => Assert.IsInstanceOfType(null, out assertTests); + action.Should().Throw(); + assertTests.Should().BeNull(); } public void IsInstanceOfTypeUsingGenericTypeWithOutParameter_WhenTypeMismatch_Fails() { - Exception ex = VerifyThrows(() => Assert.IsInstanceOfType(5, out _)); - Verify(ex.Message == "Assert.IsInstanceOfType failed. Expected type:. Actual type:."); + Action action = () => Assert.IsInstanceOfType(5, out _); + action.Should().Throw() + .And.Message.Should().Be("Assert.IsInstanceOfType failed. Expected type:. Actual type:."); } public void IsInstanceOfTypeUsingGenericType_OnSameInstance_DoesNotThrow() => Assert.IsInstanceOfType(5); @@ -120,14 +138,14 @@ public void IsInstanceOfTypeUsingGenericTypeWithOutParameter_WhenTypeMismatch_Fa public void IsInstanceOfTypeUsingGenericTypeWithOutParameter_OnSameInstance_DoesNotThrow() { Assert.IsInstanceOfType(5, out int instance); - Verify(instance == 5); + instance.Should().Be(5); } public void IsInstanceOfTypeUsingGenericTypeWithOutParameter_OnSameInstanceReferenceType_DoesNotThrow() { object testInstance = new AssertTests(); Assert.IsInstanceOfType(testInstance, out AssertTests instance); - Verify(testInstance == instance); + testInstance.Should().BeSameAs(instance); } public void IsInstanceOfTypeUsingGenericType_OnHigherInstance_DoesNotThrow() => Assert.IsInstanceOfType(5); @@ -136,7 +154,7 @@ public void IsInstanceOfTypeUsingGenericTypeWithOutParameter_OnHigherInstance_Do { object testInstance = new AssertTests(); Assert.IsInstanceOfType(testInstance, out object instance); - Verify(instance == testInstance); + instance.Should().BeSameAs(testInstance); } public void IsNotInstanceOfTypeUsingGenericType_WhenValueIsNull_DoesNotThrow() => Assert.IsNotInstanceOfType(null); diff --git a/test/UnitTests/TestFramework.UnitTests/Assertions/AssertTests.IsNull.cs b/test/UnitTests/TestFramework.UnitTests/Assertions/AssertTests.IsNull.cs index 0063810a96..32582caaf5 100644 --- a/test/UnitTests/TestFramework.UnitTests/Assertions/AssertTests.IsNull.cs +++ b/test/UnitTests/TestFramework.UnitTests/Assertions/AssertTests.IsNull.cs @@ -1,6 +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 AwesomeAssertions; + using TestFramework.ForTestingMSTest; namespace Microsoft.VisualStudio.TestPlatform.TestFramework.UnitTests; @@ -12,8 +14,9 @@ public void IsNull_PassNull_ShouldPass() public void IsNull_PassNonNull_ShouldFail() { - Exception ex = VerifyThrows(() => Assert.IsNull(new object())); - Verify(ex.Message == "Assert.IsNull failed. "); + Action action = () => Assert.IsNull(new object()); + action.Should().Throw() + .And.Message.Should().Be("Assert.IsNull failed. "); } public void IsNull_StringMessage_PassNull_ShouldPass() @@ -21,24 +24,26 @@ public void IsNull_StringMessage_PassNull_ShouldPass() public void IsNull_StringMessage_PassNonNull_ShouldFail() { - Exception ex = VerifyThrows(() => Assert.IsNull(new object(), "User-provided message")); - Verify(ex.Message == "Assert.IsNull failed. User-provided message"); + Action action = () => Assert.IsNull(new object(), "User-provided message"); + action.Should().Throw() + .And.Message.Should().Be("Assert.IsNull failed. User-provided message"); } public void IsNull_InterpolatedString_PassNull_ShouldPass() { DummyClassTrackingToStringCalls o = new(); Assert.IsNull(null, $"User-provided message {o}"); - Verify(!o.WasToStringCalled); + o.WasToStringCalled.Should().BeFalse(); } public async Task IsNull_InterpolatedString_PassNonNull_ShouldFail() { DummyClassTrackingToStringCalls o = new(); DateTime dateTime = DateTime.Now; - Exception ex = await VerifyThrowsAsync(async () => Assert.IsNull(new object(), $"User-provided message. {o}, {o,35}, {await GetHelloStringAsync()}, {new DummyIFormattable()}, {dateTime:tt}, {dateTime,5:tt}")); - Verify(ex.Message == $"Assert.IsNull failed. User-provided message. DummyClassTrackingToStringCalls, DummyClassTrackingToStringCalls, Hello, DummyIFormattable.ToString(), {string.Format(null, "{0:tt}", dateTime)}, {string.Format(null, "{0,5:tt}", dateTime)}"); - Verify(o.WasToStringCalled); + Func action = async () => Assert.IsNull(new object(), $"User-provided message. {o}, {o,35}, {await GetHelloStringAsync()}, {new DummyIFormattable()}, {dateTime:tt}, {dateTime,5:tt}"); + (await action.Should().ThrowAsync()) + .And.Message.Should().Be($"Assert.IsNull failed. User-provided message. DummyClassTrackingToStringCalls, DummyClassTrackingToStringCalls, Hello, DummyIFormattable.ToString(), {string.Format(null, "{0:tt}", dateTime)}, {string.Format(null, "{0,5:tt}", dateTime)}"); + o.WasToStringCalled.Should().BeTrue(); } public void IsNull_MessageFormat_PassNull_ShouldPass() @@ -46,8 +51,9 @@ public void IsNull_MessageFormat_PassNull_ShouldPass() public void IsNull_MessageFormat_PassNonNull_ShouldFail() { - Exception ex = VerifyThrows(() => Assert.IsNull(new object(), "User-provided message {0}", new object().GetType())); - Verify(ex.Message == "Assert.IsNull failed. User-provided message System.Object"); + Action action = () => Assert.IsNull(new object(), "User-provided message {0}", new object().GetType()); + action.Should().Throw().And + .Message.Should().Be("Assert.IsNull failed. User-provided message System.Object"); } public void IsNotNull_WhenNonNullNullableValue_DoesNotThrowAndLearnNotNull() @@ -69,7 +75,7 @@ public void IsNotNull_WhenNonNullNullableValueAndInterpolatedStringMessage_DoesN object? obj = GetObj(); DummyClassTrackingToStringCalls o = new(); Assert.IsNotNull(obj, $"my message {o}"); - Verify(!o.WasToStringCalled); + o.WasToStringCalled.Should().BeFalse(); _ = obj.ToString(); // No potential NRE warning } @@ -82,28 +88,32 @@ public void IsNotNull_WhenNonNullNullableValueAndCompositeMessage_DoesNotThrowAn public void IsNotNull_PassNull_ShouldFail() { - Exception ex = VerifyThrows(() => Assert.IsNotNull(null)); - Verify(ex.Message == "Assert.IsNotNull failed. "); + Action action = () => Assert.IsNotNull(null); + action.Should().Throw() + .And.Message.Should().Be("Assert.IsNotNull failed. "); } public void IsNotNull_StringMessage_PassNonNull_ShouldFail() { - Exception ex = VerifyThrows(() => Assert.IsNotNull(null, "User-provided message")); - Verify(ex.Message == "Assert.IsNotNull failed. User-provided message"); + Action action = () => Assert.IsNotNull(null, "User-provided message"); + action.Should().Throw() + .And.Message.Should().Be("Assert.IsNotNull failed. User-provided message"); } public async Task IsNotNull_InterpolatedString_PassNonNull_ShouldFail() { DummyClassTrackingToStringCalls o = new(); DateTime dateTime = DateTime.Now; - Exception ex = await VerifyThrowsAsync(async () => Assert.IsNotNull(null, $"User-provided message. {o}, {o,35}, {await GetHelloStringAsync()}, {new DummyIFormattable()}, {dateTime:tt}, {dateTime,5:tt}")); - Verify(ex.Message == $"Assert.IsNotNull failed. User-provided message. DummyClassTrackingToStringCalls, DummyClassTrackingToStringCalls, Hello, DummyIFormattable.ToString(), {string.Format(null, "{0:tt}", dateTime)}, {string.Format(null, "{0,5:tt}", dateTime)}"); - Verify(o.WasToStringCalled); + Func action = async () => Assert.IsNotNull(null, $"User-provided message. {o}, {o,35}, {await GetHelloStringAsync()}, {new DummyIFormattable()}, {dateTime:tt}, {dateTime,5:tt}"); + (await action.Should().ThrowAsync()) + .And.Message.Should().Be($"Assert.IsNotNull failed. User-provided message. DummyClassTrackingToStringCalls, DummyClassTrackingToStringCalls, Hello, DummyIFormattable.ToString(), {string.Format(null, "{0:tt}", dateTime)}, {string.Format(null, "{0,5:tt}", dateTime)}"); + o.WasToStringCalled.Should().BeTrue(); } public void IsNotNull_MessageFormat_PassNonNull_ShouldFail() { - Exception ex = VerifyThrows(() => Assert.IsNotNull(null, "User-provided message {0}", new object().GetType())); - Verify(ex.Message == "Assert.IsNotNull failed. User-provided message System.Object"); + Action action = () => Assert.IsNotNull(null, "User-provided message {0}", new object().GetType()); + action.Should().Throw().And + .Message.Should().Be("Assert.IsNotNull failed. User-provided message System.Object"); } } diff --git a/test/UnitTests/TestFramework.UnitTests/Assertions/AssertTests.IsTrueTests.cs b/test/UnitTests/TestFramework.UnitTests/Assertions/AssertTests.IsTrueTests.cs index dbc0444259..27bfc1eaaf 100644 --- a/test/UnitTests/TestFramework.UnitTests/Assertions/AssertTests.IsTrueTests.cs +++ b/test/UnitTests/TestFramework.UnitTests/Assertions/AssertTests.IsTrueTests.cs @@ -1,6 +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 AwesomeAssertions; + namespace Microsoft.VisualStudio.TestPlatform.TestFramework.UnitTests; public partial class AssertTests @@ -8,15 +10,17 @@ public partial class AssertTests public void IsFalseNullableBooleanShouldFailWithNull() { bool? nullBool = null; - Exception ex = VerifyThrows(() => Assert.IsFalse(nullBool)); - Verify(ex.Message == "Assert.IsFalse failed. "); + Action action = () => Assert.IsFalse(nullBool); + action.Should().Throw() + .And.Message.Should().Be("Assert.IsFalse failed. "); } public void IsFalseNullableBooleanShouldFailWithTrue() { bool? nullBool = true; - Exception ex = VerifyThrows(() => Assert.IsFalse(nullBool)); - Verify(ex.Message == "Assert.IsFalse failed. "); + Action action = () => Assert.IsFalse(nullBool); + action.Should().Throw() + .And.Message.Should().Be("Assert.IsFalse failed. "); } public void IsFalseNullableBooleanShouldNotFailWithFalse() @@ -27,8 +31,9 @@ public void IsFalseNullableBooleanShouldNotFailWithFalse() public void IsFalseBooleanShouldFailWithTrue() { - Exception ex = VerifyThrows(() => Assert.IsFalse(true)); - Verify(ex.Message == "Assert.IsFalse failed. "); + Action action = () => Assert.IsFalse(true); + action.Should().Throw() + .And.Message.Should().Be("Assert.IsFalse failed. "); } public void IsFalseBooleanShouldNotFailWithFalse() @@ -37,15 +42,17 @@ public void IsFalseBooleanShouldNotFailWithFalse() public void IsFalseNullableBooleanStringMessageShouldFailWithNull() { bool? nullBool = null; - Exception ex = VerifyThrows(() => Assert.IsFalse(nullBool, "User-provided message")); - Verify(ex.Message == "Assert.IsFalse failed. User-provided message"); + Action action = () => Assert.IsFalse(nullBool, "User-provided message"); + action.Should().Throw() + .And.Message.Should().Be("Assert.IsFalse failed. User-provided message"); } public void IsFalseNullableBooleanStringMessageShouldFailWithTrue() { bool? nullBool = true; - Exception ex = VerifyThrows(() => Assert.IsFalse(nullBool, "User-provided message")); - Verify(ex.Message == "Assert.IsFalse failed. User-provided message"); + Action action = () => Assert.IsFalse(nullBool, "User-provided message"); + action.Should().Throw() + .And.Message.Should().Be("Assert.IsFalse failed. User-provided message"); } public void IsFalseNullableBooleanStringMessageShouldNotFailWithFalse() @@ -56,8 +63,9 @@ public void IsFalseNullableBooleanStringMessageShouldNotFailWithFalse() public void IsFalseBooleanStringMessageShouldFailWithTrue() { - Exception ex = VerifyThrows(() => Assert.IsFalse(true, "User-provided message")); - Verify(ex.Message == "Assert.IsFalse failed. User-provided message"); + Action action = () => Assert.IsFalse(true, "User-provided message"); + action.Should().Throw() + .And.Message.Should().Be("Assert.IsFalse failed. User-provided message"); } public void IsFalseBooleanStringMessageShouldNotFailWithFalse() @@ -68,8 +76,9 @@ public async Task IsFalseNullableBooleanInterpolatedStringMessageShouldFailWithN bool? nullBool = null; DummyClassTrackingToStringCalls o = new(); DateTime dateTime = DateTime.Now; - Exception ex = await VerifyThrowsAsync(async () => Assert.IsFalse(nullBool, $"User-provided message. {o}, {o,35}, {await GetHelloStringAsync()}, {new DummyIFormattable()}, {dateTime:tt}, {dateTime,5:tt}")); - Verify(ex.Message == $"Assert.IsFalse failed. User-provided message. DummyClassTrackingToStringCalls, DummyClassTrackingToStringCalls, Hello, DummyIFormattable.ToString(), {string.Format(null, "{0:tt}", dateTime)}, {string.Format(null, "{0,5:tt}", dateTime)}"); + Func action = async () => Assert.IsFalse(nullBool, $"User-provided message. {o}, {o,35}, {await GetHelloStringAsync()}, {new DummyIFormattable()}, {dateTime:tt}, {dateTime,5:tt}"); + (await action.Should().ThrowAsync()) + .And.Message.Should().Be($"Assert.IsFalse failed. User-provided message. DummyClassTrackingToStringCalls, DummyClassTrackingToStringCalls, Hello, DummyIFormattable.ToString(), {string.Format(null, "{0:tt}", dateTime)}, {string.Format(null, "{0,5:tt}", dateTime)}"); } public async Task IsFalseNullableBooleanInterpolatedStringMessageShouldFailWithTrue() @@ -77,8 +86,9 @@ public async Task IsFalseNullableBooleanInterpolatedStringMessageShouldFailWithT bool? nullBool = true; DummyClassTrackingToStringCalls o = new(); DateTime dateTime = DateTime.Now; - Exception ex = await VerifyThrowsAsync(async () => Assert.IsFalse(nullBool, $"User-provided message. {o}, {o,35}, {await GetHelloStringAsync()}, {new DummyIFormattable()}, {dateTime:tt}, {dateTime,5:tt}")); - Verify(ex.Message == $"Assert.IsFalse failed. User-provided message. DummyClassTrackingToStringCalls, DummyClassTrackingToStringCalls, Hello, DummyIFormattable.ToString(), {string.Format(null, "{0:tt}", dateTime)}, {string.Format(null, "{0,5:tt}", dateTime)}"); + Func action = async () => Assert.IsFalse(nullBool, $"User-provided message. {o}, {o,35}, {await GetHelloStringAsync()}, {new DummyIFormattable()}, {dateTime:tt}, {dateTime,5:tt}"); + (await action.Should().ThrowAsync()) + .And.Message.Should().Be($"Assert.IsFalse failed. User-provided message. DummyClassTrackingToStringCalls, DummyClassTrackingToStringCalls, Hello, DummyIFormattable.ToString(), {string.Format(null, "{0:tt}", dateTime)}, {string.Format(null, "{0,5:tt}", dateTime)}"); } public void IsFalseNullableBooleanInterpolatedStringMessageShouldNotFailWithFalse() @@ -91,8 +101,9 @@ public async Task IsFalseBooleanInterpolatedStringMessageShouldFailWithTrue() { DummyClassTrackingToStringCalls o = new(); DateTime dateTime = DateTime.Now; - Exception ex = await VerifyThrowsAsync(async () => Assert.IsFalse(true, $"User-provided message. {o}, {o,35}, {await GetHelloStringAsync()}, {new DummyIFormattable()}, {dateTime:tt}, {dateTime,5:tt}")); - Verify(ex.Message == $"Assert.IsFalse failed. User-provided message. DummyClassTrackingToStringCalls, DummyClassTrackingToStringCalls, Hello, DummyIFormattable.ToString(), {string.Format(null, "{0:tt}", dateTime)}, {string.Format(null, "{0,5:tt}", dateTime)}"); + Func action = async () => Assert.IsFalse(true, $"User-provided message. {o}, {o,35}, {await GetHelloStringAsync()}, {new DummyIFormattable()}, {dateTime:tt}, {dateTime,5:tt}"); + (await action.Should().ThrowAsync()) + .And.Message.Should().Be($"Assert.IsFalse failed. User-provided message. DummyClassTrackingToStringCalls, DummyClassTrackingToStringCalls, Hello, DummyIFormattable.ToString(), {string.Format(null, "{0:tt}", dateTime)}, {string.Format(null, "{0,5:tt}", dateTime)}"); } public void IsFalseBooleanInterpolatedStringMessageShouldNotFailWithFalse() @@ -101,15 +112,17 @@ public void IsFalseBooleanInterpolatedStringMessageShouldNotFailWithFalse() public void IsFalseNullableBooleanMessageArgsShouldFailWithNull() { bool? nullBool = null; - Exception ex = VerifyThrows(() => Assert.IsFalse(nullBool, "User-provided message. Input: {0}", nullBool)); - Verify(ex.Message == "Assert.IsFalse failed. User-provided message. Input: "); + Action action = () => Assert.IsFalse(nullBool, "User-provided message. Input: {0}", nullBool); + action.Should().Throw() + .And.Message.Should().Be("Assert.IsFalse failed. User-provided message. Input: "); } public void IsFalseNullableBooleanMessageArgsShouldFailWithTrue() { bool? nullBool = true; - Exception ex = VerifyThrows(() => Assert.IsFalse(nullBool, "User-provided message. Input: {0}", nullBool)); - Verify(ex.Message == "Assert.IsFalse failed. User-provided message. Input: True"); + Action action = () => Assert.IsFalse(nullBool, "User-provided message. Input: {0}", nullBool); + action.Should().Throw() + .And.Message.Should().Be("Assert.IsFalse failed. User-provided message. Input: True"); } public void IsFalseNullableBooleanMessageArgsShouldNotFailWithFalse() @@ -120,8 +133,9 @@ public void IsFalseNullableBooleanMessageArgsShouldNotFailWithFalse() public void IsFalseBooleanMessageArgsShouldFailWithTrue() { - Exception ex = VerifyThrows(() => Assert.IsFalse(true, "User-provided message. Input: {0}", true)); - Verify(ex.Message == "Assert.IsFalse failed. User-provided message. Input: True"); + Action action = () => Assert.IsFalse(true, "User-provided message. Input: {0}", true); + action.Should().Throw() + .And.Message.Should().Be("Assert.IsFalse failed. User-provided message. Input: True"); } public void IsFalseBooleanMessageArgsShouldNotFailWithFalse() @@ -130,15 +144,17 @@ public void IsFalseBooleanMessageArgsShouldNotFailWithFalse() public void IsTrueNullableBooleanShouldFailWithNull() { bool? nullBool = null; - Exception ex = VerifyThrows(() => Assert.IsTrue(nullBool)); - Verify(ex.Message == "Assert.IsTrue failed. "); + Action action = () => Assert.IsTrue(nullBool); + action.Should().Throw() + .And.Message.Should().Be("Assert.IsTrue failed. "); } public void IsTrueNullableBooleanShouldFailWithFalse() { bool? nullBool = false; - Exception ex = VerifyThrows(() => Assert.IsTrue(nullBool)); - Verify(ex.Message == "Assert.IsTrue failed. "); + Action action = () => Assert.IsTrue(nullBool); + action.Should().Throw() + .And.Message.Should().Be("Assert.IsTrue failed. "); } public void IsTrueNullableBooleanShouldNotFailWithTrue() @@ -149,8 +165,9 @@ public void IsTrueNullableBooleanShouldNotFailWithTrue() public void IsTrueBooleanShouldFailWithFalse() { - Exception ex = VerifyThrows(() => Assert.IsTrue(false)); - Verify(ex.Message == "Assert.IsTrue failed. "); + Action action = () => Assert.IsTrue(false); + action.Should().Throw() + .And.Message.Should().Be("Assert.IsTrue failed. "); } public void IsTrueBooleanShouldNotFailWithTrue() @@ -159,15 +176,17 @@ public void IsTrueBooleanShouldNotFailWithTrue() public void IsTrueNullableBooleanStringMessageShouldFailWithNull() { bool? nullBool = null; - Exception ex = VerifyThrows(() => Assert.IsTrue(nullBool, "User-provided message")); - Verify(ex.Message == "Assert.IsTrue failed. User-provided message"); + Action action = () => Assert.IsTrue(nullBool, "User-provided message"); + action.Should().Throw() + .And.Message.Should().Be("Assert.IsTrue failed. User-provided message"); } public void IsTrueNullableBooleanStringMessageShouldFailWithFalse() { bool? nullBool = false; - Exception ex = VerifyThrows(() => Assert.IsTrue(nullBool, "User-provided message")); - Verify(ex.Message == "Assert.IsTrue failed. User-provided message"); + Action action = () => Assert.IsTrue(nullBool, "User-provided message"); + action.Should().Throw() + .And.Message.Should().Be("Assert.IsTrue failed. User-provided message"); } public void IsTrueNullableBooleanStringMessageShouldNotFailWithTrue() @@ -178,8 +197,9 @@ public void IsTrueNullableBooleanStringMessageShouldNotFailWithTrue() public void IsTrueBooleanStringMessageShouldFailWithFalse() { - Exception ex = VerifyThrows(() => Assert.IsTrue(false, "User-provided message")); - Verify(ex.Message == "Assert.IsTrue failed. User-provided message"); + Action action = () => Assert.IsTrue(false, "User-provided message"); + action.Should().Throw() + .And.Message.Should().Be("Assert.IsTrue failed. User-provided message"); } public void IsTrueBooleanStringMessageShouldNotFailWithTrue() @@ -190,8 +210,9 @@ public async Task IsTrueNullableBooleanInterpolatedStringMessageShouldFailWithNu bool? nullBool = null; DummyClassTrackingToStringCalls o = new(); DateTime dateTime = DateTime.Now; - Exception ex = await VerifyThrowsAsync(async () => Assert.IsTrue(nullBool, $"User-provided message. {o}, {o,35}, {await GetHelloStringAsync()}, {new DummyIFormattable()}, {dateTime:tt}, {dateTime,5:tt}")); - Verify(ex.Message == $"Assert.IsTrue failed. User-provided message. DummyClassTrackingToStringCalls, DummyClassTrackingToStringCalls, Hello, DummyIFormattable.ToString(), {string.Format(null, "{0:tt}", dateTime)}, {string.Format(null, "{0,5:tt}", dateTime)}"); + Func action = async () => Assert.IsTrue(nullBool, $"User-provided message. {o}, {o,35}, {await GetHelloStringAsync()}, {new DummyIFormattable()}, {dateTime:tt}, {dateTime,5:tt}"); + (await action.Should().ThrowAsync()) + .And.Message.Should().Be($"Assert.IsTrue failed. User-provided message. DummyClassTrackingToStringCalls, DummyClassTrackingToStringCalls, Hello, DummyIFormattable.ToString(), {string.Format(null, "{0:tt}", dateTime)}, {string.Format(null, "{0,5:tt}", dateTime)}"); } public async Task IsTrueNullableBooleanInterpolatedStringMessageShouldFailWithFalse() @@ -199,8 +220,9 @@ public async Task IsTrueNullableBooleanInterpolatedStringMessageShouldFailWithFa bool? nullBool = false; DummyClassTrackingToStringCalls o = new(); DateTime dateTime = DateTime.Now; - Exception ex = await VerifyThrowsAsync(async () => Assert.IsTrue(nullBool, $"User-provided message. {o}, {o,35}, {await GetHelloStringAsync()}, {new DummyIFormattable()}, {dateTime:tt}, {dateTime,5:tt}")); - Verify(ex.Message == $"Assert.IsTrue failed. User-provided message. DummyClassTrackingToStringCalls, DummyClassTrackingToStringCalls, Hello, DummyIFormattable.ToString(), {string.Format(null, "{0:tt}", dateTime)}, {string.Format(null, "{0,5:tt}", dateTime)}"); + Func action = async () => Assert.IsTrue(nullBool, $"User-provided message. {o}, {o,35}, {await GetHelloStringAsync()}, {new DummyIFormattable()}, {dateTime:tt}, {dateTime,5:tt}"); + (await action.Should().ThrowAsync()) + .And.Message.Should().Be($"Assert.IsTrue failed. User-provided message. DummyClassTrackingToStringCalls, DummyClassTrackingToStringCalls, Hello, DummyIFormattable.ToString(), {string.Format(null, "{0:tt}", dateTime)}, {string.Format(null, "{0,5:tt}", dateTime)}"); } public void IsTrueNullableBooleanInterpolatedStringMessageShouldNotFailWithTrue() @@ -213,8 +235,9 @@ public async Task IsTrueBooleanInterpolatedStringMessageShouldFailWithFalse() { DummyClassTrackingToStringCalls o = new(); DateTime dateTime = DateTime.Now; - Exception ex = await VerifyThrowsAsync(async () => Assert.IsTrue(false, $"User-provided message. {o}, {o,35}, {await GetHelloStringAsync()}, {new DummyIFormattable()}, {dateTime:tt}, {dateTime,5:tt}")); - Verify(ex.Message == $"Assert.IsTrue failed. User-provided message. DummyClassTrackingToStringCalls, DummyClassTrackingToStringCalls, Hello, DummyIFormattable.ToString(), {string.Format(null, "{0:tt}", dateTime)}, {string.Format(null, "{0,5:tt}", dateTime)}"); + Func action = async () => Assert.IsTrue(false, $"User-provided message. {o}, {o,35}, {await GetHelloStringAsync()}, {new DummyIFormattable()}, {dateTime:tt}, {dateTime,5:tt}"); + (await action.Should().ThrowAsync()) + .And.Message.Should().Be($"Assert.IsTrue failed. User-provided message. DummyClassTrackingToStringCalls, DummyClassTrackingToStringCalls, Hello, DummyIFormattable.ToString(), {string.Format(null, "{0:tt}", dateTime)}, {string.Format(null, "{0,5:tt}", dateTime)}"); } public void IsTrueBooleanInterpolatedStringMessageShouldNotFailWithTrue() @@ -223,15 +246,17 @@ public void IsTrueBooleanInterpolatedStringMessageShouldNotFailWithTrue() public void IsTrueNullableBooleanMessageArgsShouldFailWithNull() { bool? nullBool = null; - Exception ex = VerifyThrows(() => Assert.IsTrue(nullBool, "User-provided message. Input: {0}", nullBool)); - Verify(ex.Message == "Assert.IsTrue failed. User-provided message. Input: "); + Action action = () => Assert.IsTrue(nullBool, "User-provided message. Input: {0}", nullBool); + action.Should().Throw() + .And.Message.Should().Be("Assert.IsTrue failed. User-provided message. Input: "); } public void IsTrueNullableBooleanMessageArgsShouldFailWithFalse() { bool? nullBool = false; - Exception ex = VerifyThrows(() => Assert.IsTrue(nullBool, "User-provided message. Input: {0}", nullBool)); - Verify(ex.Message == "Assert.IsTrue failed. User-provided message. Input: False"); + Action action = () => Assert.IsTrue(nullBool, "User-provided message. Input: {0}", nullBool); + action.Should().Throw() + .And.Message.Should().Be("Assert.IsTrue failed. User-provided message. Input: False"); } public void IsTrueNullableBooleanMessageArgsShouldNotFailWithTrue() @@ -242,8 +267,9 @@ public void IsTrueNullableBooleanMessageArgsShouldNotFailWithTrue() public void IsTrueBooleanMessageArgsShouldFailWithFalse() { - Exception ex = VerifyThrows(() => Assert.IsTrue(false, "User-provided message. Input: {0}", false)); - Verify(ex.Message == "Assert.IsTrue failed. User-provided message. Input: False"); + Action action = () => Assert.IsTrue(false, "User-provided message. Input: {0}", false); + action.Should().Throw() + .And.Message.Should().Be("Assert.IsTrue failed. User-provided message. Input: False"); } public void IsTrueBooleanMessageArgsShouldNotFailWithTrue() diff --git a/test/UnitTests/TestFramework.UnitTests/Assertions/AssertTests.Items.cs b/test/UnitTests/TestFramework.UnitTests/Assertions/AssertTests.Items.cs index fb3ddb017f..4bd5b6f912 100644 --- a/test/UnitTests/TestFramework.UnitTests/Assertions/AssertTests.Items.cs +++ b/test/UnitTests/TestFramework.UnitTests/Assertions/AssertTests.Items.cs @@ -1,6 +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 AwesomeAssertions; + namespace Microsoft.VisualStudio.TestPlatform.TestFramework.UnitTests; public partial class AssertTests @@ -15,29 +17,32 @@ public void Count_InterpolatedString_WhenCountIsSame_ShouldPass() { DummyClassTrackingToStringCalls o = new(); Assert.HasCount(0, Array.Empty(), $"User-provided message: {o}"); - Verify(!o.WasToStringCalled); + o.WasToStringCalled.Should().BeFalse(); } public void Count_WhenCountIsNotSame_ShouldFail() { var collection = new List { 1 }; - Exception ex = VerifyThrows(() => Assert.HasCount(3, collection)); - Verify(ex.Message == "Assert.HasCount failed. Expected collection of size 3. Actual: 1. "); + Action action = () => Assert.HasCount(3, collection); + action.Should().Throw().And + .Message.Should().Be("Assert.HasCount failed. Expected collection of size 3. Actual: 1. "); } public void Count_MessageArgs_WhenCountIsNotSame_ShouldFail() { - Exception ex = VerifyThrows(() => Assert.HasCount(1, Array.Empty(), "User-provided message: System.Object type: {0}", new object().GetType())); - Verify(ex.Message == "Assert.HasCount failed. Expected collection of size 1. Actual: 0. User-provided message: System.Object type: System.Object"); + Action action = () => Assert.HasCount(1, Array.Empty(), "User-provided message: System.Object type: {0}", new object().GetType()); + action.Should().Throw().And + .Message.Should().Be("Assert.HasCount failed. Expected collection of size 1. Actual: 0. User-provided message: System.Object type: System.Object"); } public async Task Count_InterpolatedString_WhenCountIsNotSame_ShouldFail() { DummyClassTrackingToStringCalls o = new(); DateTime dateTime = DateTime.Now; - Exception ex = await VerifyThrowsAsync(async () => Assert.HasCount(1, Array.Empty(), $"User-provided message. {o}, {o,35}, {await GetHelloStringAsync()}, {new DummyIFormattable()}, {dateTime:tt}, {dateTime,5:tt}")); - Verify(ex.Message == $"Assert.HasCount failed. Expected collection of size 1. Actual: 0. User-provided message. DummyClassTrackingToStringCalls, DummyClassTrackingToStringCalls, Hello, DummyIFormattable.ToString(), {string.Format(null, "{0:tt}", dateTime)}, {string.Format(null, "{0,5:tt}", dateTime)}"); - Verify(o.WasToStringCalled); + Func action = async () => Assert.HasCount(1, Array.Empty(), $"User-provided message. {o}, {o,35}, {await GetHelloStringAsync()}, {new DummyIFormattable()}, {dateTime:tt}, {dateTime,5:tt}"); + (await action.Should().ThrowAsync()).And + .Message.Should().Be($"Assert.HasCount failed. Expected collection of size 1. Actual: 0. User-provided message. DummyClassTrackingToStringCalls, DummyClassTrackingToStringCalls, Hello, DummyIFormattable.ToString(), {string.Format(null, "{0:tt}", dateTime)}, {string.Format(null, "{0,5:tt}", dateTime)}"); + o.WasToStringCalled.Should().BeTrue(); } public void NotAny_WhenEmpty_ShouldPass() @@ -47,21 +52,23 @@ public void NotAny_InterpolatedString_WhenEmpty_ShouldPass() { DummyClassTrackingToStringCalls o = new(); Assert.IsEmpty(Array.Empty(), $"User-provided message: {o}"); - Verify(!o.WasToStringCalled); + o.WasToStringCalled.Should().BeFalse(); } public void NotAny_WhenNotEmpty_ShouldFail() { var collection = new List { 1 }; - Exception ex = VerifyThrows(() => Assert.IsEmpty(collection)); - Verify(ex.Message == "Assert.IsEmpty failed. Expected collection of size 0. Actual: 1. "); + Action action = () => Assert.IsEmpty(collection); + action.Should().Throw().And + .Message.Should().Be("Assert.IsEmpty failed. Expected collection of size 0. Actual: 1. "); } public void NotAny_MessageArgs_WhenNotEmpty_ShouldFail() { var collection = new List { 1 }; - Exception ex = VerifyThrows(() => Assert.IsEmpty(collection, "User-provided message: System.Object type: {0}", new object().GetType())); - Verify(ex.Message == "Assert.IsEmpty failed. Expected collection of size 0. Actual: 1. User-provided message: System.Object type: System.Object"); + Action action = () => Assert.IsEmpty(collection, "User-provided message: System.Object type: {0}", new object().GetType()); + action.Should().Throw().And + .Message.Should().Be("Assert.IsEmpty failed. Expected collection of size 0. Actual: 1. User-provided message: System.Object type: System.Object"); } public async Task NotAny_InterpolatedString_WhenNotEmpty_ShouldFail() @@ -69,16 +76,17 @@ public async Task NotAny_InterpolatedString_WhenNotEmpty_ShouldFail() var collection = new List { 1 }; DummyClassTrackingToStringCalls o = new(); DateTime dateTime = DateTime.Now; - Exception ex = await VerifyThrowsAsync(async () => Assert.IsEmpty(collection, $"User-provided message. {o}, {o,35}, {await GetHelloStringAsync()}, {new DummyIFormattable()}, {dateTime:tt}, {dateTime,5:tt}")); - Verify(ex.Message == $"Assert.IsEmpty failed. Expected collection of size 0. Actual: 1. User-provided message. DummyClassTrackingToStringCalls, DummyClassTrackingToStringCalls, Hello, DummyIFormattable.ToString(), {string.Format(null, "{0:tt}", dateTime)}, {string.Format(null, "{0,5:tt}", dateTime)}"); - Verify(o.WasToStringCalled); + Func action = async () => Assert.IsEmpty(collection, $"User-provided message. {o}, {o,35}, {await GetHelloStringAsync()}, {new DummyIFormattable()}, {dateTime:tt}, {dateTime,5:tt}"); + (await action.Should().ThrowAsync()).And + .Message.Should().Be($"Assert.IsEmpty failed. Expected collection of size 0. Actual: 1. User-provided message. DummyClassTrackingToStringCalls, DummyClassTrackingToStringCalls, Hello, DummyIFormattable.ToString(), {string.Format(null, "{0:tt}", dateTime)}, {string.Format(null, "{0,5:tt}", dateTime)}"); + o.WasToStringCalled.Should().BeTrue(); } public void Single_WhenOneItem_ShouldPass() { var collection = new List { 1 }; int first = Assert.ContainsSingle(collection); - Verify(first == 1); + (first == 1).Should().BeTrue(); } public void Single_InterpolatedString_WhenOneItem_ShouldPass() @@ -86,49 +94,103 @@ public void Single_InterpolatedString_WhenOneItem_ShouldPass() var collection = new List { 1 }; DummyClassTrackingToStringCalls o = new(); Assert.ContainsSingle(collection, $"User-provided message: {o}"); - Verify(!o.WasToStringCalled); + o.WasToStringCalled.Should().BeFalse(); } public void Single_WhenNoItems_ShouldFail() { - Exception ex = VerifyThrows(() => Assert.ContainsSingle(Array.Empty())); - Verify(ex.Message == "Assert.ContainsSingle failed. Expected collection of size 1. Actual: 0. "); + Action action = () => Assert.ContainsSingle(Array.Empty()); + action.Should().Throw().And + .Message.Should().Be("Assert.ContainsSingle failed. Expected collection to contain exactly one element but found 0 element(s). "); } public void Single_WhenMultipleItems_ShouldFail() { - Exception ex = VerifyThrows(() => Assert.ContainsSingle([1, 2, 3])); - Verify(ex.Message == "Assert.ContainsSingle failed. Expected collection of size 1. Actual: 3. "); + Action action = () => Assert.ContainsSingle([1, 2, 3]); + action.Should().Throw().And + .Message.Should().Be("Assert.ContainsSingle failed. Expected collection to contain exactly one element but found 3 element(s). "); } public void Single_MessageArgs_WhenNoItem_ShouldFail() { - Exception ex = VerifyThrows(() => Assert.ContainsSingle(Array.Empty(), "User-provided message: System.Object type: {0}", new object().GetType())); - Verify(ex.Message == "Assert.ContainsSingle failed. Expected collection of size 1. Actual: 0. User-provided message: System.Object type: System.Object"); + Action action = () => Assert.ContainsSingle(Array.Empty(), "User-provided message: System.Object type: {0}", new object().GetType()); + action.Should().Throw().And + .Message.Should().Be("Assert.ContainsSingle failed. Expected collection to contain exactly one element but found 0 element(s). User-provided message: System.Object type: System.Object"); } public void Single_MessageArgs_WhenMultipleItems_ShouldFail() { - Exception ex = VerifyThrows(() => Assert.ContainsSingle([1, 2, 3], "User-provided message: System.Object type: {0}", new object().GetType())); - Verify(ex.Message == "Assert.ContainsSingle failed. Expected collection of size 1. Actual: 3. User-provided message: System.Object type: System.Object"); + Action action = () => Assert.ContainsSingle([1, 2, 3], "User-provided message: System.Object type: {0}", new object().GetType()); + action.Should().Throw().And + .Message.Should().Be("Assert.ContainsSingle failed. Expected collection to contain exactly one element but found 3 element(s). User-provided message: System.Object type: System.Object"); } public async Task Single_InterpolatedString_WhenNoItem_ShouldFail() { DummyClassTrackingToStringCalls o = new(); DateTime dateTime = DateTime.Now; - Exception ex = await VerifyThrowsAsync(async () => Assert.ContainsSingle(Array.Empty(), $"User-provided message. {o}, {o,35}, {await GetHelloStringAsync()}, {new DummyIFormattable()}, {dateTime:tt}, {dateTime,5:tt}")); - Verify(ex.Message == $"Assert.ContainsSingle failed. Expected collection of size 1. Actual: 0. User-provided message. DummyClassTrackingToStringCalls, DummyClassTrackingToStringCalls, Hello, DummyIFormattable.ToString(), {string.Format(null, "{0:tt}", dateTime)}, {string.Format(null, "{0,5:tt}", dateTime)}"); - Verify(o.WasToStringCalled); + Func action = async () => Assert.ContainsSingle(Array.Empty(), $"User-provided message. {o}, {o,35}, {await GetHelloStringAsync()}, {new DummyIFormattable()}, {dateTime:tt}, {dateTime,5:tt}"); + (await action.Should().ThrowAsync()).And + .Message.Should().Be($"Assert.ContainsSingle failed. Expected collection to contain exactly one element but found 0 element(s). User-provided message. DummyClassTrackingToStringCalls, DummyClassTrackingToStringCalls, Hello, DummyIFormattable.ToString(), {string.Format(null, "{0:tt}", dateTime)}, {string.Format(null, "{0,5:tt}", dateTime)}"); + o.WasToStringCalled.Should().BeTrue(); } public async Task Single_InterpolatedString_WhenMultipleItems_ShouldFail() { DummyClassTrackingToStringCalls o = new(); DateTime dateTime = DateTime.Now; - Exception ex = await VerifyThrowsAsync(async () => Assert.ContainsSingle([1, 2, 3], $"User-provided message. {o}, {o,35}, {await GetHelloStringAsync()}, {new DummyIFormattable()}, {dateTime:tt}, {dateTime,5:tt}")); - Verify(ex.Message == $"Assert.ContainsSingle failed. Expected collection of size 1. Actual: 3. User-provided message. DummyClassTrackingToStringCalls, DummyClassTrackingToStringCalls, Hello, DummyIFormattable.ToString(), {string.Format(null, "{0:tt}", dateTime)}, {string.Format(null, "{0,5:tt}", dateTime)}"); - Verify(o.WasToStringCalled); + Func action = async () => Assert.ContainsSingle([1, 2, 3], $"User-provided message. {o}, {o,35}, {await GetHelloStringAsync()}, {new DummyIFormattable()}, {dateTime:tt}, {dateTime,5:tt}"); + (await action.Should().ThrowAsync()).And + .Message.Should().Be($"Assert.ContainsSingle failed. Expected collection to contain exactly one element but found 3 element(s). User-provided message. DummyClassTrackingToStringCalls, DummyClassTrackingToStringCalls, Hello, DummyIFormattable.ToString(), {string.Format(null, "{0:tt}", dateTime)}, {string.Format(null, "{0,5:tt}", dateTime)}"); + o.WasToStringCalled.Should().BeTrue(); + } + + public void SinglePredicate_WhenOneItemMatches_ShouldPass() + { + var collection = new List { 1, 3, 5 }; + int result = Assert.ContainsSingle(x => x == 3, collection); + result.Should().Be(3); + } + + public void SinglePredicate_WithMessage_WhenOneItemMatches_ShouldPass() + { + var collection = new List { "apple", "banana", "cherry" }; +#pragma warning disable CA1865 // Use char overload - not netfx + string result = Assert.ContainsSingle(x => x.StartsWith("b", StringComparison.Ordinal), collection, "Expected one item starting with 'b'"); +#pragma warning restore CA1865 + result.Should().Be("banana"); + } + + public void SinglePredicate_WhenNoItemMatches_ShouldFail() + { + var collection = new List { 1, 3, 5 }; + Action action = () => Assert.ContainsSingle(x => x % 2 == 0, collection); + action.Should().Throw().And + .Message.Should().Be("Assert.ContainsSingle failed. Expected exactly one item to match the predicate but found 0 item(s). "); + } + + public void SinglePredicate_WhenMultipleItemsMatch_ShouldFail() + { + var collection = new List { 2, 4, 6 }; + Action action = () => Assert.ContainsSingle(x => x % 2 == 0, collection); + action.Should().Throw().And + .Message.Should().Be("Assert.ContainsSingle failed. Expected exactly one item to match the predicate but found 3 item(s). "); + } + + public void SinglePredicate_Message_WhenNoItemMatches_ShouldFail() + { + var collection = new List { 1, 3, 5 }; + Action action = () => Assert.ContainsSingle(x => x % 2 == 0, collection, "No even numbers found: test"); + action.Should().Throw().And + .Message.Should().Be("Assert.ContainsSingle failed. Expected exactly one item to match the predicate but found 0 item(s). No even numbers found: test"); + } + + public void SinglePredicate_Message_WhenMultipleItemsMatch_ShouldFail() + { + var collection = new List { 2, 4, 6 }; + Action action = () => Assert.ContainsSingle(x => x % 2 == 0, collection, "Too many even numbers: test"); + action.Should().Throw().And + .Message.Should().Be("Assert.ContainsSingle failed. Expected exactly one item to match the predicate but found 3 item(s). Too many even numbers: test"); } public void Any_WhenOneItem_ShouldPass() @@ -147,34 +209,37 @@ public void Any_InterpolatedString_WhenAnyOneItem_ShouldPass() { DummyClassTrackingToStringCalls o = new(); Assert.IsNotEmpty([1], $"User-provided message: {o}"); - Verify(!o.WasToStringCalled); + o.WasToStringCalled.Should().BeFalse(); } public void Any_InterpolatedString_WhenMultipleItems_ShouldPass() { DummyClassTrackingToStringCalls o = new(); Assert.IsNotEmpty([1, 2, 3], $"User-provided message: {o}"); - Verify(!o.WasToStringCalled); + o.WasToStringCalled.Should().BeFalse(); } public void Any_WhenNoItem_ShouldFail() { - Exception ex = VerifyThrows(() => Assert.IsNotEmpty(Array.Empty())); - Verify(ex.Message == "Assert.IsNotEmpty failed. Expected collection to contain any item but it is empty. "); + Action action = () => Assert.IsNotEmpty(Array.Empty()); + action.Should().Throw().And + .Message.Should().Be("Assert.IsNotEmpty failed. Expected collection to contain any item but it is empty. "); } public void Any_MessageArgs_WhenNoItem_ShouldFail() { - Exception ex = VerifyThrows(() => Assert.IsNotEmpty(Array.Empty(), "User-provided message: System.Object type: {0}", new object().GetType())); - Verify(ex.Message == "Assert.IsNotEmpty failed. Expected collection to contain any item but it is empty. User-provided message: System.Object type: System.Object"); + Action action = () => Assert.IsNotEmpty(Array.Empty(), "User-provided message: System.Object type: {0}", new object().GetType()); + action.Should().Throw().And + .Message.Should().Be("Assert.IsNotEmpty failed. Expected collection to contain any item but it is empty. User-provided message: System.Object type: System.Object"); } public async Task Any_InterpolatedString_WhenNoItem_ShouldFail() { DummyClassTrackingToStringCalls o = new(); DateTime dateTime = DateTime.Now; - Exception ex = await VerifyThrowsAsync(async () => Assert.IsNotEmpty(Array.Empty(), $"User-provided message. {o}, {o,35}, {await GetHelloStringAsync()}, {new DummyIFormattable()}, {dateTime:tt}, {dateTime,5:tt}")); - Verify(ex.Message == $"Assert.IsNotEmpty failed. Expected collection to contain any item but it is empty. User-provided message. DummyClassTrackingToStringCalls, DummyClassTrackingToStringCalls, Hello, DummyIFormattable.ToString(), {string.Format(null, "{0:tt}", dateTime)}, {string.Format(null, "{0,5:tt}", dateTime)}"); - Verify(o.WasToStringCalled); + Func action = async () => Assert.IsNotEmpty(Array.Empty(), $"User-provided message. {o}, {o,35}, {await GetHelloStringAsync()}, {new DummyIFormattable()}, {dateTime:tt}, {dateTime,5:tt}"); + (await action.Should().ThrowAsync()).And + .Message.Should().Be($"Assert.IsNotEmpty failed. Expected collection to contain any item but it is empty. User-provided message. DummyClassTrackingToStringCalls, DummyClassTrackingToStringCalls, Hello, DummyIFormattable.ToString(), {string.Format(null, "{0:tt}", dateTime)}, {string.Format(null, "{0,5:tt}", dateTime)}"); + o.WasToStringCalled.Should().BeTrue(); } } diff --git a/test/UnitTests/TestFramework.UnitTests/Assertions/AssertTests.ThrowsExceptionTests.cs b/test/UnitTests/TestFramework.UnitTests/Assertions/AssertTests.ThrowsExceptionTests.cs index 52b1fd9620..cdbc092f5e 100644 --- a/test/UnitTests/TestFramework.UnitTests/Assertions/AssertTests.ThrowsExceptionTests.cs +++ b/test/UnitTests/TestFramework.UnitTests/Assertions/AssertTests.ThrowsExceptionTests.cs @@ -1,6 +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 AwesomeAssertions; + namespace Microsoft.VisualStudio.TestPlatform.TestFramework.UnitTests; public partial class AssertTests @@ -9,40 +11,46 @@ public partial class AssertTests // See https://github.com/dotnet/sdk/issues/25373 public void ThrowAssertFailedDoesNotThrowIfMessageContainsInvalidStringFormatComposite() { - Exception ex = VerifyThrows(() => Assert.ThrowAssertFailed("name", "{")); - Verify(ex.Message.Contains("name failed. {")); + Action action = () => Assert.ThrowAssertFailed("name", "{"); + action.Should().Throw() + .And.Message.Should().Contain("name failed. {"); } #endregion #region ThrowsException tests public void ThrowsExceptionWithLambdaExpressionsShouldThrowAssertionOnNoException() { - Exception ex = VerifyThrows(() => Assert.ThrowsException(() => { })); - Verify(ex.Message.Equals("Assert.ThrowsException failed. Expected exception type: but no exception was thrown. ", StringComparison.Ordinal)); + Action action = () => Assert.ThrowsException(() => { }); + action.Should().Throw() + .And.Message.Should().Be("Assert.ThrowsException failed. Expected exception type: but no exception was thrown. "); } public void ThrowsExceptionWithLambdaExpressionsShouldThrowAssertionOnWrongException() { - Exception ex = VerifyThrows(() => Assert.ThrowsException(() => throw new FormatException())); - Verify(ex.Message.Equals("Assert.ThrowsException failed. Expected exception type:. Actual exception type:. ", StringComparison.Ordinal)); + Action action = () => Assert.ThrowsException(() => throw new FormatException()); + action.Should().Throw() + .And.Message.Should().Be("Assert.ThrowsException failed. Expected exception type:. Actual exception type:. "); } public void ThrowsException_FuncArgument_AllowsToReturnNull() { - Exception ex = VerifyThrows(() => Assert.ThrowsException(() => null)); - Verify(ex.Message.Equals("Assert.ThrowsException failed. Expected exception type: but no exception was thrown. ", StringComparison.Ordinal)); + Action action = () => Assert.ThrowsException(() => null); + action.Should().Throw() + .And.Message.Should().Be("Assert.ThrowsException failed. Expected exception type: but no exception was thrown. "); } public void ThrowsException_FuncArgumentOverloadWithMessage_AllowsToReturnNull() { - Exception ex = VerifyThrows(() => Assert.ThrowsException(() => null, "message")); - Verify(ex.Message.Equals("Assert.ThrowsException failed. Expected exception type: but no exception was thrown. message", StringComparison.Ordinal)); + Action action = () => Assert.ThrowsException(() => null, "message"); + action.Should().Throw() + .And.Message.Should().Be("Assert.ThrowsException failed. Expected exception type: but no exception was thrown. message"); } public void ThrowsException_FuncArgumentOverloadWithMessagesAndParameters_AllowsToReturnNull() { - Exception ex = VerifyThrows(() => Assert.ThrowsException(() => null, "message {0}", 1)); - Verify(ex.Message.Equals("Assert.ThrowsException failed. Expected exception type: but no exception was thrown. message 1", StringComparison.Ordinal)); + Action action = () => Assert.ThrowsException(() => null, "message {0}", 1); + action.Should().Throw() + .And.Message.Should().Be("Assert.ThrowsException failed. Expected exception type: but no exception was thrown. message 1"); } #endregion @@ -64,13 +72,12 @@ public void ThrowsExceptionAsyncShouldThrowAssertionOnNoException() { Task t = Assert.ThrowsExceptionAsync( async () => await Task.Delay(5).ConfigureAwait(false)); - Exception ex = VerifyThrows(t.Wait); - - Exception? innerException = ex.InnerException; + Action action = t.Wait; + AggregateException ex = action.Should().Throw().Which; - Verify(innerException is not null); - Verify(typeof(AssertFailedException) == innerException.GetType()); - Verify(innerException.Message.Equals("Assert.ThrowsExceptionAsync failed. Expected exception type: but no exception was thrown. ", StringComparison.Ordinal)); + ex.InnerException.Should().NotBeNull(); + ex.InnerException!.Should().BeOfType(); + ex.InnerException!.Message.Should().Be("Assert.ThrowsExceptionAsync failed. Expected exception type: but no exception was thrown. "); } public void ThrowsExceptionAsyncShouldThrowAssertionOnWrongException() @@ -81,13 +88,12 @@ public void ThrowsExceptionAsyncShouldThrowAssertionOnWrongException() await Task.Delay(5).ConfigureAwait(false); throw new FormatException(); }); - Exception ex = VerifyThrows(t.Wait); - - Exception? innerException = ex.InnerException; + Action action = t.Wait; + AggregateException ex = action.Should().Throw().Which; - Verify(innerException is not null); - Assert.AreEqual(typeof(AssertFailedException), innerException.GetType()); - Verify(innerException.Message.Equals("Assert.ThrowsExceptionAsync failed. Expected exception type:. Actual exception type:. ", StringComparison.Ordinal)); + ex.InnerException.Should().NotBeNull(); + ex.InnerException!.Should().BeOfType(); + ex.InnerException!.Message.Should().Be("Assert.ThrowsExceptionAsync failed. Expected exception type:. Actual exception type:. "); } public void ThrowsExceptionAsyncWithMessageShouldThrowAssertionOnNoException() @@ -95,13 +101,12 @@ 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); + Action action = t.Wait; + AggregateException ex = action.Should().Throw().Which; - Exception? innerException = ex.InnerException; - - Verify(innerException is not null); - Assert.AreEqual(typeof(AssertFailedException), innerException.GetType()); - Verify(innerException.Message.Equals("Assert.ThrowsExceptionAsync failed. Expected exception type: but no exception was thrown. The world is not on fire.", StringComparison.Ordinal)); + ex.InnerException.Should().NotBeNull(); + ex.InnerException!.Should().BeOfType(); + ex.InnerException!.Message.Should().Be("Assert.ThrowsExceptionAsync failed. Expected exception type: but no exception was thrown. The world is not on fire."); } public void ThrowsExceptionAsyncWithMessageShouldThrowAssertionOnWrongException() @@ -113,13 +118,12 @@ public void ThrowsExceptionAsyncWithMessageShouldThrowAssertionOnWrongException( throw new FormatException(); }, "Happily ever after."); - Exception ex = VerifyThrows(t.Wait); - - Exception? innerException = ex.InnerException; + Action action = t.Wait; + AggregateException ex = action.Should().Throw().Which; - Verify(innerException is not null); - Assert.AreEqual(typeof(AssertFailedException), innerException.GetType()); - Verify(innerException.Message.Equals("Assert.ThrowsExceptionAsync failed. Expected exception type:. Actual exception type:. Happily ever after.", StringComparison.Ordinal)); + ex.InnerException.Should().NotBeNull(); + ex.InnerException!.Should().BeOfType(); + ex.InnerException!.Message.Should().Be("Assert.ThrowsExceptionAsync failed. Expected exception type:. Actual exception type:. Happily ever after."); } public void ThrowsExceptionAsyncWithMessageAndParamsShouldThrowOnNullAction() @@ -130,12 +134,11 @@ static void A() t.Wait(); } - Exception ex = VerifyThrows(A); - - Exception? innerException = ex.InnerException; + Action action = A; + AggregateException ex = action.Should().Throw().Which; - Verify(innerException is not null); - Verify(typeof(ArgumentNullException) == innerException.GetType()); + ex.InnerException.Should().NotBeNull(); + ex.InnerException!.Should().BeOfType(); } public void ThrowsExceptionAsyncWithMessageAndParamsShouldThrowOnNullMessage() @@ -146,12 +149,11 @@ static void A() t.Wait(); } - Exception ex = VerifyThrows(A); + Action action = A; + AggregateException ex = action.Should().Throw().Which; - Exception? innerException = ex.InnerException; - - Verify(innerException is not null); - Verify(typeof(ArgumentNullException) == innerException.GetType()); + ex.InnerException.Should().NotBeNull(); + ex.InnerException!.Should().BeOfType(); } public void ThrowsExceptionAsyncWithMessageAndParamsShouldThrowAssertionOnNoException() @@ -162,13 +164,12 @@ public void ThrowsExceptionAsyncWithMessageAndParamsShouldThrowAssertionOnNoExce "ta", "da", 123); - Exception ex = VerifyThrows(t.Wait); - - Exception? innerException = ex.InnerException; + Action action = t.Wait; + AggregateException ex = action.Should().Throw().Which; - Verify(innerException is not null); - Assert.AreEqual(typeof(AssertFailedException), innerException.GetType()); - Verify(innerException.Message.Equals("Assert.ThrowsExceptionAsync failed. Expected exception type: but no exception was thrown. The world is not on fire ta.da-123.", StringComparison.Ordinal)); + ex.InnerException.Should().NotBeNull(); + Assert.AreEqual(typeof(AssertFailedException), ex.InnerException.GetType()); + ex.InnerException!.Message.Should().Be("Assert.ThrowsExceptionAsync failed. Expected exception type: but no exception was thrown. The world is not on fire ta.da-123."); } public void ThrowsExceptionAsyncWithMessageAndParamsShouldThrowAssertionOnWrongException() @@ -182,13 +183,12 @@ public void ThrowsExceptionAsyncWithMessageAndParamsShouldThrowAssertionOnWrongE "Happily ever after. {0} {1}.", "The", "End"); - Exception ex = VerifyThrows(t.Wait); - - Exception? innerException = ex.InnerException; + Action action = t.Wait; + AggregateException ex = action.Should().Throw().Which; - Verify(innerException is not null); - Assert.AreEqual(typeof(AssertFailedException), innerException.GetType()); - Verify(innerException.Message.Equals("Assert.ThrowsExceptionAsync failed. Expected exception type:. Actual exception type:. Happily ever after. The End.", StringComparison.Ordinal)); + ex.InnerException.Should().NotBeNull(); + Assert.AreEqual(typeof(AssertFailedException), ex.InnerException.GetType()); + ex.InnerException!.Message.Should().Be("Assert.ThrowsExceptionAsync failed. Expected exception type:. Actual exception type:. Happily ever after. The End."); } #endregion @@ -198,15 +198,39 @@ public void Throws_WhenExceptionIsDerivedFromExpectedType_ShouldNotThrow() public void Throws_WhenExceptionIsNotExpectedType_ShouldThrow() { static void Action() => Assert.Throws(() => throw new Exception()); - Exception ex = VerifyThrows(Action); - Verify(ex is AssertFailedException); + Action action = Action; + action.Should().Throw(); + } + + public void Throws_WithInterpolation() + { + static string GetString() => throw new Exception(); + +#pragma warning disable IDE0200 // Remove unnecessary lambda expression - intentionally testing overload resolution for this case. + Exception ex = Assert.Throws(() => GetString(), $"Hello {GetString()}"); +#pragma warning restore IDE0200 // Remove unnecessary lambda expression + Exception ex2 = Assert.Throws(GetString, $"Hello {GetString()}"); + ex.Should().NotBeNull(); + ex2.Should().NotBeNull(); + } + + public void ThrowsExactly_WithInterpolation() + { + static string GetString() => throw new Exception(); + +#pragma warning disable IDE0200 // Remove unnecessary lambda expression - intentionally testing overload resolution for this case. + Exception ex = Assert.ThrowsExactly(() => GetString(), $"Hello {GetString()}"); +#pragma warning restore IDE0200 // Remove unnecessary lambda expression + Exception ex2 = Assert.ThrowsExactly(GetString, $"Hello {GetString()}"); + ex.Should().NotBeNull(); + ex2.Should().NotBeNull(); } public void ThrowsExactly_WhenExceptionIsDerivedFromExpectedType_ShouldThrow() { static void Action() => Assert.ThrowsExactly(() => throw new ArgumentNullException()); - Exception ex = VerifyThrows(Action); - Verify(ex is AssertFailedException); + Action action = Action; + action.Should().Throw(); } public void ThrowsExactly_WhenExceptionExpectedType_ShouldNotThrow() @@ -218,22 +242,21 @@ public async Task ThrowsAsync_WhenExceptionIsDerivedFromExpectedType_ShouldNotTh public void ThrowsAsync_WhenExceptionIsNotExpectedType_ShouldThrow() { Task t = Assert.ThrowsAsync(() => throw new Exception()); - Exception ex = VerifyThrows(t.Wait); - Assert.IsInstanceOfType(ex.InnerException, out AssertFailedException assertFailedException); - Assert.AreEqual("Assert.ThrowsAsync failed. Expected exception type:. Actual exception type:. ", assertFailedException.Message); + Action action = t.Wait; + AggregateException ex = action.Should().Throw().Which; + ex.InnerException.Should().BeOfType(); + ex.InnerException!.Message.Should().Be("Assert.ThrowsAsync failed. Expected exception type:. Actual exception type:. "); } public void ThrowsExactlyAsync_WhenExceptionIsDerivedFromExpectedType_ShouldThrow() { Task t = Assert.ThrowsExactlyAsync(() => throw new ArgumentNullException()); - Exception ex = VerifyThrows(t.Wait); - Assert.IsInstanceOfType(ex.InnerException, out AssertFailedException assertFailedException); - Assert.AreEqual("Assert.ThrowsExactlyAsync failed. Expected exception type:. Actual exception type:. ", assertFailedException.Message); + Action action = t.Wait; + AggregateException ex = action.Should().Throw().Which; + ex.InnerException.Should().BeOfType(); + ex.InnerException!.Message.Should().Be("Assert.ThrowsExactlyAsync failed. Expected exception type:. Actual exception type:. "); } - public async Task ThrowsExactlyAsync_WhenExceptionExpectedType_ShouldNotThrow() - => await Assert.ThrowsExactlyAsync(() => throw new ArgumentNullException()); - public void Throws_WithMessageBuilder_Passes() { bool wasBuilderCalled = false; @@ -243,39 +266,42 @@ public void Throws_WithMessageBuilder_Passes() return "message constructed via builder."; }); - Verify(!wasBuilderCalled); + wasBuilderCalled.Should().BeFalse(); } public void Throws_WithMessageBuilder_FailsBecauseNoException() { bool wasBuilderCalled = false; Exception? exceptionPassedToBuilder = null; - AssertFailedException assertFailedEx = VerifyThrows(() => Assert.Throws(() => { }, messageBuilder: ex => + Action action = () => Assert.Throws(() => { }, messageBuilder: ex => { wasBuilderCalled = true; exceptionPassedToBuilder = ex; return "message constructed via builder."; - })); + }); + action.Should().Throw() + .And.Message.Should().Be("Assert.Throws failed. Expected exception type: but no exception was thrown. message constructed via builder."); - Verify(wasBuilderCalled); - Verify(exceptionPassedToBuilder is null); - Verify(assertFailedEx.Message == "Assert.Throws failed. Expected exception type: but no exception was thrown. message constructed via builder."); + wasBuilderCalled.Should().BeTrue(); + exceptionPassedToBuilder.Should().BeNull(); } public void Throws_WithMessageBuilder_FailsBecauseTypeMismatch() { bool wasBuilderCalled = false; Exception? exceptionPassedToBuilder = null; - AssertFailedException assertFailedEx = VerifyThrows(() => Assert.Throws(() => throw new ArgumentOutOfRangeException("MyParamNameHere"), messageBuilder: ex => + Action action = () => Assert.Throws(() => throw new ArgumentOutOfRangeException("MyParamNameHere"), messageBuilder: ex => { wasBuilderCalled = true; exceptionPassedToBuilder = ex; return "message constructed via builder."; - })); + }); + action.Should().Throw() + .And.Message.Should().Be("Assert.Throws failed. Expected exception type:. Actual exception type:. message constructed via builder."); - Verify(wasBuilderCalled); - Verify(exceptionPassedToBuilder is ArgumentOutOfRangeException { ParamName: "MyParamNameHere" }); - Verify(assertFailedEx.Message == "Assert.Throws failed. Expected exception type:. Actual exception type:. message constructed via builder."); + wasBuilderCalled.Should().BeTrue(); + exceptionPassedToBuilder.Should().BeOfType(); + ((ArgumentOutOfRangeException)exceptionPassedToBuilder!).ParamName.Should().Be("MyParamNameHere"); } public void ThrowsExactly_WithMessageBuilder_Passes() @@ -287,39 +313,42 @@ public void ThrowsExactly_WithMessageBuilder_Passes() return "message constructed via builder."; }); - Verify(!wasBuilderCalled); + wasBuilderCalled.Should().BeFalse(); } public void ThrowsExactly_WithMessageBuilder_FailsBecauseNoException() { bool wasBuilderCalled = false; Exception? exceptionPassedToBuilder = null; - AssertFailedException assertFailedEx = VerifyThrows(() => Assert.ThrowsExactly(() => { }, messageBuilder: ex => + Action action = () => Assert.ThrowsExactly(() => { }, messageBuilder: ex => { wasBuilderCalled = true; exceptionPassedToBuilder = ex; return "message constructed via builder."; - })); + }); + action.Should().Throw() + .And.Message.Should().Be("Assert.ThrowsExactly failed. Expected exception type: but no exception was thrown. message constructed via builder."); - Verify(wasBuilderCalled); - Verify(exceptionPassedToBuilder is null); - Verify(assertFailedEx.Message == "Assert.ThrowsExactly failed. Expected exception type: but no exception was thrown. message constructed via builder."); + wasBuilderCalled.Should().BeTrue(); + exceptionPassedToBuilder.Should().BeNull(); } public void ThrowsExactly_WithMessageBuilder_FailsBecauseTypeMismatch() { bool wasBuilderCalled = false; Exception? exceptionPassedToBuilder = null; - AssertFailedException assertFailedEx = VerifyThrows(() => Assert.ThrowsExactly(() => throw new ArgumentOutOfRangeException("MyParamNameHere"), messageBuilder: ex => + Action action = () => Assert.ThrowsExactly(() => throw new ArgumentOutOfRangeException("MyParamNameHere"), messageBuilder: ex => { wasBuilderCalled = true; exceptionPassedToBuilder = ex; return "message constructed via builder."; - })); + }); + action.Should().Throw() + .And.Message.Should().Be("Assert.ThrowsExactly failed. Expected exception type:. Actual exception type:. message constructed via builder."); - Verify(wasBuilderCalled); - Verify(exceptionPassedToBuilder is ArgumentOutOfRangeException { ParamName: "MyParamNameHere" }); - Verify(assertFailedEx.Message == "Assert.ThrowsExactly failed. Expected exception type:. Actual exception type:. message constructed via builder."); + wasBuilderCalled.Should().BeTrue(); + exceptionPassedToBuilder.Should().BeOfType(); + ((ArgumentOutOfRangeException)exceptionPassedToBuilder!).ParamName.Should().Be("MyParamNameHere"); } public async Task ThrowsAsync_WithMessageBuilder_Passes() @@ -331,39 +360,42 @@ await Assert.ThrowsAsync(() => Task.FromException(new Arg return "message constructed via builder."; }); - Verify(!wasBuilderCalled); + wasBuilderCalled.Should().BeFalse(); } public async Task ThrowsAsync_WithMessageBuilder_FailsBecauseNoException() { bool wasBuilderCalled = false; Exception? exceptionPassedToBuilder = null; - AssertFailedException assertFailedEx = await VerifyThrowsAsync(async () => await Assert.ThrowsAsync(() => Task.CompletedTask, messageBuilder: ex => + Func action = async () => await Assert.ThrowsAsync(() => Task.CompletedTask, messageBuilder: ex => { wasBuilderCalled = true; exceptionPassedToBuilder = ex; return "message constructed via builder."; - })); + }); + (await action.Should().ThrowAsync()) + .And.Message.Should().Be("Assert.ThrowsAsync failed. Expected exception type: but no exception was thrown. message constructed via builder."); - Verify(wasBuilderCalled); - Verify(exceptionPassedToBuilder is null); - Verify(assertFailedEx.Message == "Assert.ThrowsAsync failed. Expected exception type: but no exception was thrown. message constructed via builder."); + wasBuilderCalled.Should().BeTrue(); + exceptionPassedToBuilder.Should().BeNull(); } public async Task ThrowsAsync_WithMessageBuilder_FailsBecauseTypeMismatch() { bool wasBuilderCalled = false; Exception? exceptionPassedToBuilder = null; - AssertFailedException assertFailedEx = await VerifyThrowsAsync(async () => await Assert.ThrowsAsync(() => Task.FromException(new ArgumentOutOfRangeException("MyParamNameHere")), messageBuilder: ex => + Func action = async () => await Assert.ThrowsAsync(() => Task.FromException(new ArgumentOutOfRangeException("MyParamNameHere")), messageBuilder: ex => { wasBuilderCalled = true; exceptionPassedToBuilder = ex; return "message constructed via builder."; - })); + }); + (await action.Should().ThrowAsync()) + .And.Message.Should().Be("Assert.ThrowsAsync failed. Expected exception type:. Actual exception type:. message constructed via builder."); - Verify(wasBuilderCalled); - Verify(exceptionPassedToBuilder is ArgumentOutOfRangeException { ParamName: "MyParamNameHere" }); - Verify(assertFailedEx.Message == "Assert.ThrowsAsync failed. Expected exception type:. Actual exception type:. message constructed via builder."); + wasBuilderCalled.Should().BeTrue(); + exceptionPassedToBuilder.Should().BeOfType(); + ((ArgumentOutOfRangeException)exceptionPassedToBuilder!).ParamName.Should().Be("MyParamNameHere"); } public async Task ThrowsExactlyAsync_WithMessageBuilder_Passes() @@ -375,38 +407,41 @@ await Assert.ThrowsExactlyAsync(() => Task.FromException( return "message constructed via builder."; }); - Verify(!wasBuilderCalled); + wasBuilderCalled.Should().BeFalse(); } public async Task ThrowsExactlyAsync_WithMessageBuilder_FailsBecauseNoException() { bool wasBuilderCalled = false; Exception? exceptionPassedToBuilder = null; - AssertFailedException assertFailedEx = await VerifyThrowsAsync(async () => await Assert.ThrowsExactlyAsync(() => Task.CompletedTask, messageBuilder: ex => + Func action = async () => await Assert.ThrowsExactlyAsync(() => Task.CompletedTask, messageBuilder: ex => { wasBuilderCalled = true; exceptionPassedToBuilder = ex; return "message constructed via builder."; - })); + }); + (await action.Should().ThrowAsync()) + .And.Message.Should().Be("Assert.ThrowsExactlyAsync failed. Expected exception type: but no exception was thrown. message constructed via builder."); - Verify(wasBuilderCalled); - Verify(exceptionPassedToBuilder is null); - Verify(assertFailedEx.Message == "Assert.ThrowsExactlyAsync failed. Expected exception type: but no exception was thrown. message constructed via builder."); + wasBuilderCalled.Should().BeTrue(); + exceptionPassedToBuilder.Should().BeNull(); } public async Task ThrowsExactlyAsync_WithMessageBuilder_FailsBecauseTypeMismatch() { bool wasBuilderCalled = false; Exception? exceptionPassedToBuilder = null; - AssertFailedException assertFailedEx = await VerifyThrowsAsync(async () => await Assert.ThrowsExactlyAsync(() => Task.FromException(new ArgumentOutOfRangeException("MyParamNameHere")), messageBuilder: ex => + Func action = async () => await Assert.ThrowsExactlyAsync(() => Task.FromException(new ArgumentOutOfRangeException("MyParamNameHere")), messageBuilder: ex => { wasBuilderCalled = true; exceptionPassedToBuilder = ex; return "message constructed via builder."; - })); + }); + (await action.Should().ThrowAsync()) + .And.Message.Should().Be("Assert.ThrowsExactlyAsync failed. Expected exception type:. Actual exception type:. message constructed via builder."); - Verify(wasBuilderCalled); - Verify(exceptionPassedToBuilder is ArgumentOutOfRangeException { ParamName: "MyParamNameHere" }); - Verify(assertFailedEx.Message == "Assert.ThrowsExactlyAsync failed. Expected exception type:. Actual exception type:. message constructed via builder."); + wasBuilderCalled.Should().BeTrue(); + exceptionPassedToBuilder.Should().BeOfType(); + ((ArgumentOutOfRangeException)exceptionPassedToBuilder!).ParamName.Should().Be("MyParamNameHere"); } } diff --git a/test/UnitTests/TestFramework.UnitTests/Assertions/AssertTests.cs b/test/UnitTests/TestFramework.UnitTests/Assertions/AssertTests.cs index 91873731a5..9e5ac645e7 100644 --- a/test/UnitTests/TestFramework.UnitTests/Assertions/AssertTests.cs +++ b/test/UnitTests/TestFramework.UnitTests/Assertions/AssertTests.cs @@ -1,24 +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 AwesomeAssertions; + namespace Microsoft.VisualStudio.TestPlatform.TestFramework.UnitTests; public partial class AssertTests { - #region That tests - public void ThatShouldReturnAnInstanceOfAssert() => Verify(Assert.That is not null); + #region Instance tests + public void InstanceShouldReturnAnInstanceOfAssert() => Assert.That.Should().NotBeNull(); - public void ThatShouldCacheAssertInstance() => Verify(ReferenceEquals(Assert.That, Assert.That)); + public void InstanceShouldCacheAssertInstance() => Assert.That.Should().BeSameAs(Assert.That); #endregion #region ReplaceNullChars tests public void ReplaceNullCharsShouldReturnStringIfNullOrEmpty() { - Verify(Assert.ReplaceNullChars(null) == null); - Verify(Assert.ReplaceNullChars(string.Empty) == string.Empty); + Assert.ReplaceNullChars(null).Should().BeNull(); + Assert.ReplaceNullChars(string.Empty).Should().BeSameAs(string.Empty); } - public void ReplaceNullCharsShouldReplaceNullCharsInAString() => Verify(Assert.ReplaceNullChars("The quick brown fox \0 jumped over the la\0zy dog\0") == "The quick brown fox \\0 jumped over the la\\0zy dog\\0"); + public void ReplaceNullCharsShouldReplaceNullCharsInAString() => Assert.ReplaceNullChars("The quick brown fox \0 jumped over the la\0zy dog\0").Should().Be("The quick brown fox \\0 jumped over the la\\0zy dog\\0"); #endregion #region BuildUserMessage tests @@ -26,16 +28,39 @@ public void ReplaceNullCharsShouldReturnStringIfNullOrEmpty() // See https://github.com/dotnet/sdk/issues/25373 public void BuildUserMessageThrowsWhenMessageContainsInvalidStringFormatComposite() { - Exception ex = VerifyThrows(() => Assert.BuildUserMessage("{", "arg")); - Verify(ex is FormatException); + Action act = () => Assert.BuildUserMessage("{", "arg"); + act.Should().Throw(); } // See https://github.com/dotnet/sdk/issues/25373 public void BuildUserMessageDoesNotThrowWhenMessageContainsInvalidStringFormatCompositeAndNoArgumentsPassed() { string message = Assert.BuildUserMessage("{"); - Verify(message == "{"); + message.Should().Be("{"); + } + #endregion + + #region Obsolete methods tests +#if DEBUG + public void ObsoleteEqualsMethodThrowsAssertFailedException() + { +#pragma warning disable CS0618 // Type or member is obsolete + Action act = () => Assert.Equals("test", "test"); +#pragma warning restore CS0618 // Type or member is obsolete + act.Should().Throw() + .WithMessage("*Assert.Equals should not be used for Assertions*"); + } + + public void ObsoleteReferenceEqualsMethodThrowsAssertFailedException() + { + object obj = new(); +#pragma warning disable CS0618 // Type or member is obsolete + Action act = () => Assert.ReferenceEquals(obj, obj); +#pragma warning restore CS0618 // Type or member is obsolete + act.Should().Throw() + .WithMessage("*Assert.ReferenceEquals should not be used for Assertions*"); } +#endif #endregion private static Task GetHelloStringAsync() diff --git a/test/UnitTests/TestFramework.UnitTests/Assertions/CollectionAssertTests.cs b/test/UnitTests/TestFramework.UnitTests/Assertions/CollectionAssertTests.cs index 877e51b57a..07c8782bc6 100644 --- a/test/UnitTests/TestFramework.UnitTests/Assertions/CollectionAssertTests.cs +++ b/test/UnitTests/TestFramework.UnitTests/Assertions/CollectionAssertTests.cs @@ -3,15 +3,17 @@ using System.Collections.ObjectModel; +using AwesomeAssertions; + using TestFramework.ForTestingMSTest; namespace Microsoft.VisualStudio.TestPlatform.TestFramework.UnitTests.Assertions; public class CollectionAssertTests : TestContainer { - public void ThatShouldReturnAnInstanceOfCollectionAssert() => Verify(CollectionAssert.That is not null); + public void InstanceShouldReturnAnInstanceOfCollectionAssert() => (CollectionAssert.That is not null).Should().BeTrue(); - public void ThatShouldCacheCollectionAssertInstance() => Verify(CollectionAssert.That == CollectionAssert.That); + public void InstanceShouldCacheCollectionAssertInstance() => (CollectionAssert.That == CollectionAssert.That).Should().BeTrue(); public void CollectionAssertContainsNullabilityPostConditions() { @@ -106,7 +108,7 @@ public void CollectionAssertAllItemsAreUniqueMessageParametersNullabilityPostCon public void CollectionAssertIsSubsetOfNullabilityPostConditions() { ICollection? collection = GetCollection(); - ICollection? superset = GetMatchingSuperSet(); + ICollection? superset = GetMatchingSuperset(); CollectionAssert.IsSubsetOf(collection, superset); _ = collection.Count; // no warning _ = superset.Count; // no warning @@ -115,7 +117,7 @@ public void CollectionAssertIsSubsetOfNullabilityPostConditions() public void CollectionAssertIsSubsetOfMessageNullabilityPostConditions() { ICollection? collection = GetCollection(); - ICollection? superset = GetMatchingSuperSet(); + ICollection? superset = GetMatchingSuperset(); CollectionAssert.IsSubsetOf(collection, superset, "message"); _ = collection.Count; // no warning _ = superset.Count; // no warning @@ -124,16 +126,55 @@ public void CollectionAssertIsSubsetOfMessageNullabilityPostConditions() public void CollectionAssertIsSubsetOfMessageParametersNullabilityPostConditions() { ICollection? collection = GetCollection(); - ICollection? superset = GetMatchingSuperSet(); + ICollection? superset = GetMatchingSuperset(); CollectionAssert.IsSubsetOf(collection, superset, "message format {0} {1}", 1, 2); _ = collection.Count; // no warning _ = superset.Count; // no warning } + public void CollectionAssertIsSubsetOf_ReturnedSubsetValueMessage_ThrowExceptionMessage() + { + // Arrange + ICollection? collection = GetSubsetCollection(); + ICollection? superset = GetSupersetCollection(); + + // Act + Action action = () => CollectionAssert.IsSubsetOf(collection, superset); + + // Assert + action.Should().Throw().WithMessage("CollectionAssert.IsSubsetOf failed. Element(s) is/are not present in the collection."); + } + + public void CollectionAssertIsSubsetOf_WithMessageParams_ReturnedSubsetValueMessage_ThrowExceptionMessage() + { + // Arrange + ICollection? collection = GetSubsetCollection(); + ICollection? superset = GetSupersetCollection(); + + // Act + Action action = () => CollectionAssert.IsSubsetOf(collection, superset, "message format {0} {1}", 1, 2); + + // Assert + action.Should().Throw().WithMessage("CollectionAssert.IsSubsetOf failed. Element(s) is/are not present in the collection. message format 1 2"); + } + + public void CollectionAssertIsSubsetOf_WithMessage_ReturnedSubsetValueMessage_ThrowExceptionMessage() + { + // Arrange + ICollection? collection = GetSubsetCollection(); + ICollection? superset = GetSupersetCollection(); + + // Act + Action action = () => CollectionAssert.IsSubsetOf(collection, superset, "message"); + + // Assert + action.Should().Throw().WithMessage("CollectionAssert.IsSubsetOf failed. Element(s) is/are not present in the collection. message"); + } + public void CollectionAssertIsNotSubsetOfNullabilityPostConditions() { ICollection? collection = GetCollection(); - ICollection? superset = GetNotMatchingSuperSet(); + ICollection? superset = GetNotMatchingSuperset(); CollectionAssert.IsNotSubsetOf(collection, superset); _ = collection.Count; // no warning _ = superset.Count; // no warning @@ -142,7 +183,7 @@ public void CollectionAssertIsNotSubsetOfNullabilityPostConditions() public void CollectionAssertIsNotSubsetOfMessageNullabilityPostConditions() { ICollection? collection = GetCollection(); - ICollection? superset = GetNotMatchingSuperSet(); + ICollection? superset = GetNotMatchingSuperset(); CollectionAssert.IsNotSubsetOf(collection, superset, "message"); _ = collection.Count; // no warning _ = superset.Count; // no warning @@ -151,7 +192,7 @@ public void CollectionAssertIsNotSubsetOfMessageNullabilityPostConditions() public void CollectionAssertIsNotSubsetOfMessageParametersNullabilityPostConditions() { ICollection? collection = GetCollection(); - ICollection? superset = GetNotMatchingSuperSet(); + ICollection? superset = GetNotMatchingSuperset(); CollectionAssert.IsNotSubsetOf(collection, superset, "message format {0} {1}", 1, 2); _ = collection.Count; // no warning _ = superset.Count; // no warning @@ -204,14 +245,16 @@ public void CollectionAssertAreEqual_WithNestedDiffSizedArrays_Fails() { int[][] expected = [[1, 2], [3, 4], [5, 6], [7, 8], [9]]; int[][] actual = [[1, 2], [999, 999, 999, 999, 999], [5, 6], [], [9]]; - VerifyThrows(() => CollectionAssert.AreEqual(expected, actual)); + Action action = () => CollectionAssert.AreEqual(expected, actual); + action.Should().Throw(); } public void CollectionAssertAreEqual_WithCaseSensetiveComparer_Fails() { List expected = ["one", "two"]; List actual = ["ONE", "tWo"]; - VerifyThrows(() => CollectionAssert.AreEqual(expected, actual, StringComparer.Ordinal)); + Action action = () => CollectionAssert.AreEqual(expected, actual, StringComparer.Ordinal); + action.Should().Throw(); } public void CollectionAssertAreEqualComparerMessageNullabilityPostConditions() @@ -253,7 +296,8 @@ public void CollectionAssertAreEqual_NotEqualNestedLists_Fails() ICollection? collection1 = GetNestedLists(); ICollection? collection2 = GetNotMatchingNestedLists(); - VerifyThrows(() => CollectionAssert.AreEqual(collection1, collection2)); + Action action = () => CollectionAssert.AreEqual(collection1, collection2); + action.Should().Throw(); } public void CollectionAssertAreEqual_EqualNonICollectionInnerCollection_Passes() @@ -269,7 +313,8 @@ public void CollectionAssertAreEqual_NotEqualNonICollectionInnerCollection_Fails ICollection? collection1 = GetNonICollectionInnerCollection(); ICollection? collection2 = GetNotMatchingGetNonICollectionInnerCollection(); - VerifyThrows(() => CollectionAssert.AreEqual(collection1, collection2)); + Action action = () => CollectionAssert.AreEqual(collection1, collection2); + action.Should().Throw(); } public void CollectionAssertAreNotEqual_NotEqualNestedLists_Passes() @@ -284,7 +329,8 @@ public void CollectionAssertAreNotEqual_WithIgnoreCaseComparer_Fails() { List expected = ["one", "two"]; List actual = ["ONE", "tWo"]; - VerifyThrows(() => CollectionAssert.AreNotEqual(expected, actual, StringComparer.OrdinalIgnoreCase)); + Action action = () => CollectionAssert.AreNotEqual(expected, actual, StringComparer.OrdinalIgnoreCase); + action.Should().Throw(); } public void CollectionAssertAreNotEqual_WithCaseSensitiveComparer_Passes() @@ -299,7 +345,8 @@ public void CollectionAssertAreNotEqual_EqualNestedLists_Fails() ICollection? collection1 = GetNestedLists(); ICollection? collection2 = GetNestedLists(); - VerifyThrows(() => CollectionAssert.AreNotEqual(collection1, collection2)); + Action action = () => CollectionAssert.AreNotEqual(collection1, collection2); + action.Should().Throw(); } public void CollectionAssertAreNotEqual_EqualNonICollectionInnerCollection_Fails() @@ -307,7 +354,8 @@ public void CollectionAssertAreNotEqual_EqualNonICollectionInnerCollection_Fails ICollection? collection1 = GetNonICollectionInnerCollection(); ICollection? collection2 = GetNonICollectionInnerCollection(); - VerifyThrows(() => CollectionAssert.AreNotEqual(collection1, collection2)); + Action action = () => CollectionAssert.AreNotEqual(collection1, collection2); + action.Should().Throw(); } public void CollectionAssertAreNotEqual_NotEqualNonICollectionInnerCollection_Passes() @@ -329,7 +377,7 @@ public void CollectionAssertAreNotEqual_NotEqualDeeplyNestedLists_Passes() public void CollectionAssertAreNotEqualComparerNullabilityPostConditions() { ICollection? collection1 = GetCollection(); - ICollection? collection2 = GetMatchingSuperSet(); + ICollection? collection2 = GetMatchingSuperset(); IComparer? comparer = GetComparer(); CollectionAssert.AreNotEqual(collection1, collection2, comparer); comparer.ToString(); // no warning @@ -338,7 +386,7 @@ public void CollectionAssertAreNotEqualComparerNullabilityPostConditions() public void CollectionAssertAreNotEqualComparerMessageNullabilityPostConditions() { ICollection? collection1 = GetCollection(); - ICollection? collection2 = GetMatchingSuperSet(); + ICollection? collection2 = GetMatchingSuperset(); IComparer? comparer = GetComparer(); CollectionAssert.AreNotEqual(collection1, collection2, comparer, "message"); comparer.ToString(); // no warning @@ -347,7 +395,7 @@ public void CollectionAssertAreNotEqualComparerMessageNullabilityPostConditions( public void CollectionAssertAreNotEqualComparerMessageParametersNullabilityPostConditions() { ICollection? collection1 = GetCollection(); - ICollection? collection2 = GetMatchingSuperSet(); + ICollection? collection2 = GetMatchingSuperset(); IComparer? comparer = GetComparer(); CollectionAssert.AreNotEqual(collection1, collection2, comparer, "message format {0} {1}", 1, 2); comparer.ToString(); // no warning @@ -355,8 +403,8 @@ public void CollectionAssertAreNotEqualComparerMessageParametersNullabilityPostC public void CollectionAssertAreEquivalent_SameItemsWithDifferentOrder_DoesNotThrow() { - ICollection? collection1 = GetMatchingSuperSet(); - ICollection? collection2 = GetReversedMatchingSuperSet(); + ICollection? collection1 = GetMatchingSuperset(); + ICollection? collection2 = GetReversedMatchingSuperset(); CollectionAssert.AreEquivalent(collection1, collection2); } @@ -372,114 +420,124 @@ public void CollectionAssertAreEquivalent_WithMatchingNullableSets_DoesNotThrow( public void CollectionAssertAreEquivalent_FailWhenNotEquivalent_WithMessage() { ICollection? collection1 = GetCollection(); - ICollection? collection2 = GetMatchingSuperSet(); - Exception ex = VerifyThrows(() => CollectionAssert.AreEquivalent(collection1, collection2, "message")); - Verify(ex.Message.Contains("message")); + ICollection? collection2 = GetMatchingSuperset(); + Action action = () => CollectionAssert.AreEquivalent(collection1, collection2, "message"); + action.Should().Throw().And + .Message.Should().Contain("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")); + ICollection? collection2 = GetMatchingSuperset(); + Action action = () => CollectionAssert.AreEquivalent(collection1, collection2, "message format {0} {1}", 1, 2); + action.Should().Throw().And. + Message.Should().Contain("message"); } public void CollectionAssertAreEquivalent_WithInsensitiveCaseComparer_DoesNotThrow() { - ICollection? collection1 = GetMatchingSuperSet(); - ICollection? collection2 = GetLettersCaseMismatchingSuperSet(); + 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")); + ICollection? collection2 = GetLettersCaseMismatchingSuperset(); + Action action = () => CollectionAssert.AreEquivalent(collection1?.Cast(), collection2?.Cast(), new CaseInsensitiveEqualityComparer(), "message"); + action.Should().Throw().And + .Message.Should().Contain("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")); + ICollection? collection2 = GetLettersCaseMismatchingSuperset(); + Action action = () => CollectionAssert.AreEquivalent(collection1?.Cast(), collection2?.Cast(), new CaseInsensitiveEqualityComparer(), "message format {0} {1}", 1, 2); + action.Should().Throw().And + .Message.Should().Contain("message"); } public void CollectionAssertAreNotEquivalent_SameItemsWithDifferentOrder_DoesNotThrow() { ICollection? collection1 = GetCollection(); - ICollection? collection2 = GetMatchingSuperSet(); + 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")); + ICollection? collection1 = GetReversedMatchingSuperset(); + ICollection? collection2 = GetMatchingSuperset(); + Action action = () => CollectionAssert.AreNotEquivalent(collection1, collection2, "message"); + action.Should().Throw().And + .Message.Should().Contain("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")); + ICollection? collection1 = GetReversedMatchingSuperset(); + ICollection? collection2 = GetMatchingSuperset(); + Action action = () => CollectionAssert.AreNotEquivalent(collection1, collection2, "message format {0} {1}", 1, 2); + action.Should().Throw().And + .Message.Should().Contain("message"); } public void CollectionAssertAreNotEquivalent_WithInsensitiveCaseComparer_DoesNotThrow() { ICollection? collection1 = GetCollection(); - ICollection? collection2 = GetMatchingSuperSet(); + 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")); + ICollection? collection1 = GetMatchingSuperset(); + ICollection? collection2 = GetLettersCaseMismatchingSuperset(); + Action action = () => CollectionAssert.AreNotEquivalent(collection1?.Cast(), collection2?.Cast(), new CaseInsensitiveNotEqualityComparer(), "message"); + action.Should().Throw() + .And.Message.Should().Contain("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")); + ICollection? collection1 = GetMatchingSuperset(); + ICollection? collection2 = GetLettersCaseMismatchingSuperset(); + Action action = () => CollectionAssert.AreNotEquivalent(collection1?.Cast(), collection2?.Cast(), new CaseInsensitiveNotEqualityComparer(), "message format {0} {1}", 1, 2); + action.Should().Throw() + .And.Message.Should().Contain("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")); + Action action = () => CollectionAssert.AreNotEquivalent(null, null, new CaseInsensitiveNotEqualityComparer(), "message format {0} {1}", 1, 2); + action.Should().Throw() + .And.Message.Should().Contain("message"); } public void CollectionAssertAreEqualWithoutUserMessage_FailsWithGoodMessage() { - Exception ex = VerifyThrows(() => CollectionAssert.AreEqual(new[] { 1, 2, 3 }, new[] { 1, 5, 3 })); - Assert.AreEqual( - """ + Action action = () => CollectionAssert.AreEqual(new[] { 1, 2, 3 }, new[] { 1, 5, 3 }); + action.Should().Throw() + .And.Message.Should().Be(""" CollectionAssert.AreEqual failed. Element at index 1 do not match. Expected: 2 Actual: 5 - """, ex.Message); + """); } public void CollectionAssertAreEqualWithUserMessage_FailsWithGoodMessage() { - Exception ex = VerifyThrows(() => CollectionAssert.AreEqual(new[] { 1, 2, 3 }, new[] { 1, 5, 3 }, "User-provided message")); - Assert.AreEqual( + Action action = () => CollectionAssert.AreEqual(new[] { 1, 2, 3 }, new[] { 1, 5, 3 }, "User-provided message"); + action.Should().Throw() + .And.Message.Should().Be( """ CollectionAssert.AreEqual failed. User-provided message. Element at index 1 do not match. Expected: 2 Actual: 5 - """, ex.Message); + """); } #pragma warning disable CA1859 // Use concrete types when possible for improved performance @@ -488,7 +546,7 @@ private static List GenerateDeeplyNestedCollection(int depth) { if (depth == 0) { - return new List { new ReadOnlyCollection(Enumerable.Range(1, 10).ToList()) }; + return [new ReadOnlyCollection([.. Enumerable.Range(1, 10)])]; } var nestedCollection = new List(); @@ -502,17 +560,21 @@ private static List GenerateDeeplyNestedCollection(int depth) private ICollection? GetCollection() => new[] { "item" }; + private ICollection? GetSubsetCollection() => new[] { "iem", "a", "b" }; + private object? GetMatchingElement() => "item"; private object? GetNotMatchingElement() => "not found"; - private ICollection? GetMatchingSuperSet() => new[] { "item", "item2" }; + private ICollection? GetMatchingSuperset() => new[] { "item", "item2" }; + + private ICollection? GetSupersetCollection() => new[] { "item", "item2", "c", "d" }; - private ICollection? GetLettersCaseMismatchingSuperSet() => new[] { "Item", "iTem2" }; + private ICollection? GetLettersCaseMismatchingSuperset() => new[] { "Item", "iTem2" }; - private ICollection? GetReversedMatchingSuperSet() => new[] { "item2", "item" }; + private ICollection? GetReversedMatchingSuperset() => new[] { "item2", "item" }; - private ICollection? GetNotMatchingSuperSet() => new[] { "item3" }; + private ICollection? GetNotMatchingSuperset() => new[] { "item3" }; private ICollection? GetNestedLists() => new List> { @@ -528,14 +590,14 @@ private static List GenerateDeeplyNestedCollection(int depth) private ICollection? GetNonICollectionInnerCollection() => new List> { - new(new List { 1, 2 }), - new(new List { 3, 4 }), + new([1, 2]), + new([3, 4]), }; private ICollection? GetNotMatchingGetNonICollectionInnerCollection() => new List> { - new(new List { 6, 5 }), - new(new List { 3, 4 }), + new([6, 5]), + new([3, 4]), }; private Type? GetStringType() => typeof(string); @@ -561,4 +623,27 @@ private class CaseInsensitiveNotEqualityComparer : IEqualityComparer public int GetHashCode(string obj) => obj.ToUpperInvariant().GetHashCode(); } + + #region Obsolete methods tests +#if DEBUG + public void ObsoleteEqualsMethodThrowsAssertFailedException() + { +#pragma warning disable CS0618 // Type or member is obsolete + Action action = () => CollectionAssert.Equals("test", "test"); +#pragma warning restore CS0618 // Type or member is obsolete + action.Should().Throw() + .And.Message.Should().Contain("CollectionAssert.Equals should not be used for Assertions"); + } + + public void ObsoleteReferenceEqualsMethodThrowsAssertFailedException() + { + object obj = new(); +#pragma warning disable CS0618 // Type or member is obsolete + Action action = () => CollectionAssert.ReferenceEquals(obj, obj); +#pragma warning restore CS0618 // Type or member is obsolete + action.Should().Throw() + .And.Message.Should().Contain("CollectionAssert.ReferenceEquals should not be used for Assertions"); + } +#endif + #endregion } diff --git a/test/UnitTests/TestFramework.UnitTests/Assertions/StringAssertTests.cs b/test/UnitTests/TestFramework.UnitTests/Assertions/StringAssertTests.cs index 7ecf1a3ac8..9c2e82789a 100644 --- a/test/UnitTests/TestFramework.UnitTests/Assertions/StringAssertTests.cs +++ b/test/UnitTests/TestFramework.UnitTests/Assertions/StringAssertTests.cs @@ -1,46 +1,52 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. +using AwesomeAssertions; + using TestFramework.ForTestingMSTest; namespace Microsoft.VisualStudio.TestPlatform.TestFramework.UnitTests.Assertions; public class StringAssertTests : TestContainer { - public void ThatShouldReturnAnInstanceOfStringAssert() => Verify(StringAssert.That is not null); + public void InstanceShouldReturnAnInstanceOfStringAssert() => StringAssert.That.Should().NotBeNull(); - public void ThatShouldCacheStringAssertInstance() => Verify(StringAssert.That == StringAssert.That); + public void InstanceShouldCacheStringAssertInstance() => StringAssert.That.Should().BeSameAs(StringAssert.That); 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")); + Action action = () => StringAssert.Contains(actual, notInString); + action.Should().Throw() + .And.Message.Should().Contain("StringAssert.Contains failed"); } 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")); + Action action = () => StringAssert.StartsWith(actual, notInString); + action.Should().Throw() + .And.Message.Should().Contain("StringAssert.StartsWith failed"); } 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")); + Action action = () => StringAssert.EndsWith(actual, notInString); + action.Should().Throw() + .And.Message.Should().Contain("StringAssert.EndsWith failed"); } 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")); + Action action = () => StringAssert.DoesNotMatch(actual, doesMatch); + action.Should().Throw() + .And.Message.Should().Contain("StringAssert.DoesNotMatch failed"); } public void StringAssertContainsIgnoreCase_DoesNotThrow() @@ -67,23 +73,25 @@ public void StringAssertEndsWithIgnoreCase_DoesNotThrow() // See https://github.com/dotnet/sdk/issues/25373 public void StringAssertContainsDoesNotThrowFormatException() { - Exception ex = VerifyThrows(() => StringAssert.Contains(":-{", "x")); - Verify(ex.Message.Contains("StringAssert.Contains failed")); + Action action = () => StringAssert.Contains(":-{", "x"); + action.Should().Throw() + .And.Message.Should().Contain("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")); + Action action = () => StringAssert.Contains("{", "x", "message {0}", "arg"); + action.Should().Throw() + .And.Message.Should().Contain("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")); - Verify(ex is FormatException); + Action action = () => StringAssert.Contains("a", "b", "message {{0}", "arg"); + action.Should().Throw(); } public void StringAssertContainsNullabilitiesPostConditions() @@ -311,4 +319,27 @@ public void StringAssertDoesNotMatchMessageParametersNullabilitiesPostConditions private Regex? GetMatchingPattern() => new("some*"); private Regex? GetNonMatchingPattern() => new("something"); + + #region Obsolete methods tests +#if DEBUG + public void ObsoleteEqualsMethodThrowsAssertFailedException() + { +#pragma warning disable CS0618 // Type or member is obsolete + Action action = () => StringAssert.Equals("test", "test"); +#pragma warning restore CS0618 // Type or member is obsolete + action.Should().Throw() + .And.Message.Should().Contain("StringAssert.Equals should not be used for Assertions"); + } + + public void ObsoleteReferenceEqualsMethodThrowsAssertFailedException() + { + object obj = new(); +#pragma warning disable CS0618 // Type or member is obsolete + Action action = () => StringAssert.ReferenceEquals(obj, obj); +#pragma warning restore CS0618 // Type or member is obsolete + action.Should().Throw() + .And.Message.Should().Contain("StringAssert.ReferenceEquals should not be used for Assertions"); + } +#endif + #endregion } diff --git a/test/UnitTests/TestFramework.UnitTests/Attributes/CIConditionAttributeTests.cs b/test/UnitTests/TestFramework.UnitTests/Attributes/CIConditionAttributeTests.cs new file mode 100644 index 0000000000..1a0b6687d5 --- /dev/null +++ b/test/UnitTests/TestFramework.UnitTests/Attributes/CIConditionAttributeTests.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 AwesomeAssertions; + +using Moq; + +using TestFramework.ForTestingMSTest; + +namespace UnitTestFramework.Tests; + +/// +/// Tests for class CIConditionAttribute. +/// +public class CIConditionAttributeTests : TestContainer +{ + public void Constructor_SetsCorrectMode() + { + // Arrange + var mockEnvironment = new Mock(); + + // Act + var includeAttribute = new CIConditionAttribute(ConditionMode.Include, mockEnvironment.Object); + var excludeAttribute = new CIConditionAttribute(ConditionMode.Exclude, mockEnvironment.Object); + + // Assert + includeAttribute.Mode.Should().Be(ConditionMode.Include); + excludeAttribute.Mode.Should().Be(ConditionMode.Exclude); + } + + public void GroupName_ReturnsCorrectValue() + { + // Arrange + var mockEnvironment = new Mock(); + var attribute = new CIConditionAttribute(ConditionMode.Include, mockEnvironment.Object); + + // Act & Assert + attribute.GroupName.Should().Be(nameof(CIConditionAttribute)); + } + + public void IgnoreMessage_IncludeMode_ReturnsCorrectMessage() + { + // Arrange + var mockEnvironment = new Mock(); + var attribute = new CIConditionAttribute(ConditionMode.Include, mockEnvironment.Object); + + // Act & Assert + attribute.IgnoreMessage.Should().Be("Test is only supported in CI environments"); + } + + public void IgnoreMessage_ExcludeMode_ReturnsCorrectMessage() + { + // Arrange + var mockEnvironment = new Mock(); + var attribute = new CIConditionAttribute(ConditionMode.Exclude, mockEnvironment.Object); + + // Act & Assert + attribute.IgnoreMessage.Should().Be("Test is not supported in CI environments"); + } + + public void ShouldRun_IncludeMode_WhenNotInCI_ReturnsFalse() + { + // Arrange + var mockEnvironment = new Mock(); + var attribute = new CIConditionAttribute(ConditionMode.Include, mockEnvironment.Object); + + // Act & Assert + attribute.ShouldRun.Should().BeFalse(); + } + + public void ShouldRun_ExcludeMode_WhenNotInCI_ReturnsFalse() + { + // Arrange + var mockEnvironment = new Mock(); + var attribute = new CIConditionAttribute(ConditionMode.Exclude, mockEnvironment.Object); + + // Act & Assert + attribute.ShouldRun.Should().BeFalse(); + } + + public void ShouldRun_IncludeMode_WhenInCI_GitHub_ReturnsTrue() + { + // Arrange + var mockEnvironment = new Mock(); + mockEnvironment.Setup(e => e.GetEnvironmentVariable("GITHUB_ACTIONS")).Returns("true"); + var attribute = new CIConditionAttribute(ConditionMode.Include, mockEnvironment.Object); + + // Act & Assert + attribute.ShouldRun.Should().BeTrue(); + } + + public void ShouldRun_ExcludeMode_WhenInCI_GitHub_ReturnsTrue() + { + // Arrange + var mockEnvironment = new Mock(); + mockEnvironment.Setup(e => e.GetEnvironmentVariable("GITHUB_ACTIONS")).Returns("true"); + var attribute = new CIConditionAttribute(ConditionMode.Exclude, mockEnvironment.Object); + + // Act & Assert + attribute.ShouldRun.Should().BeTrue(); + } + + public void ShouldRun_IncludeMode_WhenInCI_AzurePipelines_ReturnsTrue() + { + // Arrange + var mockEnvironment = new Mock(); + mockEnvironment.Setup(e => e.GetEnvironmentVariable("TF_BUILD")).Returns("true"); + var attribute = new CIConditionAttribute(ConditionMode.Include, mockEnvironment.Object); + + // Act & Assert + attribute.ShouldRun.Should().BeTrue(); + } + + public void ShouldRun_IncludeMode_WhenInCI_AppVeyor_ReturnsTrue() + { + // Arrange + var mockEnvironment = new Mock(); + mockEnvironment.Setup(e => e.GetEnvironmentVariable("APPVEYOR")).Returns("true"); + var attribute = new CIConditionAttribute(ConditionMode.Include, mockEnvironment.Object); + + // Act & Assert + attribute.ShouldRun.Should().BeTrue(); + } + + public void ShouldRun_IncludeMode_WhenInCI_Travis_ReturnsTrue() + { + // Arrange + var mockEnvironment = new Mock(); + mockEnvironment.Setup(e => e.GetEnvironmentVariable("TRAVIS")).Returns("true"); + var attribute = new CIConditionAttribute(ConditionMode.Include, mockEnvironment.Object); + + // Act & Assert + attribute.ShouldRun.Should().BeTrue(); + } + + public void ShouldRun_IncludeMode_WhenInCI_CircleCI_ReturnsTrue() + { + // Arrange + var mockEnvironment = new Mock(); + mockEnvironment.Setup(e => e.GetEnvironmentVariable("CIRCLECI")).Returns("true"); + var attribute = new CIConditionAttribute(ConditionMode.Include, mockEnvironment.Object); + + // Act & Assert + attribute.ShouldRun.Should().BeTrue(); + } + + public void ShouldRun_IncludeMode_WhenInCI_Generic_ReturnsTrue() + { + // Arrange + var mockEnvironment = new Mock(); + mockEnvironment.Setup(e => e.GetEnvironmentVariable("CI")).Returns("true"); + var attribute = new CIConditionAttribute(ConditionMode.Include, mockEnvironment.Object); + + // Act & Assert + attribute.ShouldRun.Should().BeTrue(); + } + + public void ShouldRun_IncludeMode_WhenInCI_TeamCity_ReturnsTrue() + { + // Arrange + var mockEnvironment = new Mock(); + mockEnvironment.Setup(e => e.GetEnvironmentVariable("TEAMCITY_VERSION")).Returns("2023.11"); + var attribute = new CIConditionAttribute(ConditionMode.Include, mockEnvironment.Object); + + // Act & Assert + attribute.ShouldRun.Should().BeTrue(); + } + + public void ShouldRun_IncludeMode_WhenInCI_Jenkins_ReturnsTrue() + { + // Arrange + var mockEnvironment = new Mock(); + mockEnvironment.Setup(e => e.GetEnvironmentVariable("BUILD_ID")).Returns("123"); + mockEnvironment.Setup(e => e.GetEnvironmentVariable("BUILD_URL")).Returns("http://jenkins.example.com/job/test/123/"); + var attribute = new CIConditionAttribute(ConditionMode.Include, mockEnvironment.Object); + + // Act & Assert + attribute.ShouldRun.Should().BeTrue(); + } + + public void ShouldRun_IncludeMode_WhenInCI_AWSCodeBuild_ReturnsTrue() + { + // Arrange + var mockEnvironment = new Mock(); + mockEnvironment.Setup(e => e.GetEnvironmentVariable("CODEBUILD_BUILD_ID")).Returns("codebuild-demo-project:b1e6661e-e4f2-4156-9ab9-82a19EXAMPLE"); + mockEnvironment.Setup(e => e.GetEnvironmentVariable("AWS_REGION")).Returns("us-east-1"); + var attribute = new CIConditionAttribute(ConditionMode.Include, mockEnvironment.Object); + + // Act & Assert + attribute.ShouldRun.Should().BeTrue(); + } + + public void ShouldRun_IncludeMode_WhenInCI_GoogleCloudBuild_ReturnsTrue() + { + // Arrange + var mockEnvironment = new Mock(); + mockEnvironment.Setup(e => e.GetEnvironmentVariable("BUILD_ID")).Returns("abc-123-def-456"); + mockEnvironment.Setup(e => e.GetEnvironmentVariable("PROJECT_ID")).Returns("my-project"); + var attribute = new CIConditionAttribute(ConditionMode.Include, mockEnvironment.Object); + + // Act & Assert + attribute.ShouldRun.Should().BeTrue(); + } + + public void ShouldRun_IncludeMode_WhenInCI_JetBrainsSpace_ReturnsTrue() + { + // Arrange + var mockEnvironment = new Mock(); + mockEnvironment.Setup(e => e.GetEnvironmentVariable("JB_SPACE_API_URL")).Returns("https://mycompany.jetbrains.space"); + var attribute = new CIConditionAttribute(ConditionMode.Include, mockEnvironment.Object); + + // Act & Assert + attribute.ShouldRun.Should().BeTrue(); + } + + public void ShouldRun_Jenkins_RequiresBothVariables() + { + // Arrange + var mockEnvironment = new Mock(); + mockEnvironment.Setup(e => e.GetEnvironmentVariable("BUILD_ID")).Returns("123"); + // BUILD_URL not set - should return null by default + var attribute = new CIConditionAttribute(ConditionMode.Include, mockEnvironment.Object); + + // Act & Assert - Should not detect as CI since both variables are required + attribute.ShouldRun.Should().BeFalse(); + } + + public void ShouldRun_AWSCodeBuild_RequiresBothVariables() + { + // Arrange + var mockEnvironment = new Mock(); + mockEnvironment.Setup(e => e.GetEnvironmentVariable("CODEBUILD_BUILD_ID")).Returns("codebuild-demo-project:b1e6661e-e4f2-4156-9ab9-82a19EXAMPLE"); + // AWS_REGION not set - should return null by default + var attribute = new CIConditionAttribute(ConditionMode.Include, mockEnvironment.Object); + + // Act & Assert - Should not detect as CI since both variables are required + attribute.ShouldRun.Should().BeFalse(); + } + + public void ShouldRun_GoogleCloudBuild_RequiresBothVariables() + { + // Arrange + var mockEnvironment = new Mock(); + mockEnvironment.Setup(e => e.GetEnvironmentVariable("BUILD_ID")).Returns("abc-123-def-456"); + // PROJECT_ID not set - should return null by default + var attribute = new CIConditionAttribute(ConditionMode.Include, mockEnvironment.Object); + + // Act & Assert - Should not detect as CI since both variables are required + attribute.ShouldRun.Should().BeFalse(); + } + + public void ShouldRun_BooleanVariable_RequiresTrueValue() + { + // Arrange + var mockEnvironment = new Mock(); + mockEnvironment.Setup(e => e.GetEnvironmentVariable("CI")).Returns("false"); + var attribute = new CIConditionAttribute(ConditionMode.Include, mockEnvironment.Object); + + // Act & Assert - Should not detect as CI since value is false + attribute.ShouldRun.Should().BeFalse(); + } + + public void ShouldRun_BooleanVariable_RequiresValidBooleanValue() + { + // Arrange + var mockEnvironment = new Mock(); + mockEnvironment.Setup(e => e.GetEnvironmentVariable("CI")).Returns("invalid"); + var attribute = new CIConditionAttribute(ConditionMode.Include, mockEnvironment.Object); + + // Act & Assert - Should not detect as CI since value is not a valid boolean + attribute.ShouldRun.Should().BeFalse(); + } +} diff --git a/test/UnitTests/TestFramework.UnitTests/Attributes/DataRowAttributeTests.cs b/test/UnitTests/TestFramework.UnitTests/Attributes/DataRowAttributeTests.cs index 176a5d279d..7231b56a82 100644 --- a/test/UnitTests/TestFramework.UnitTests/Attributes/DataRowAttributeTests.cs +++ b/test/UnitTests/TestFramework.UnitTests/Attributes/DataRowAttributeTests.cs @@ -1,6 +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 AwesomeAssertions; + using Moq; using TestFramework.ForTestingMSTest; @@ -15,42 +17,42 @@ public void DefaultConstructorSetsEmptyArrayPassed() { var dataRow = new DataRowAttribute(); - Verify(Array.Empty().SequenceEqual(dataRow.Data)); + dataRow.Data.Should().BeEquivalentTo(Array.Empty()); } public void ConstructorShouldSetDataPassed() { var dataRow = new DataRowAttribute("mercury"); - Verify(new object[] { "mercury" }.SequenceEqual(dataRow.Data)); + dataRow.Data.Should().BeEquivalentTo(new object[] { "mercury" }); } public void ConstructorShouldSetNullDataPassed() { var dataRow = new DataRowAttribute(null); - Verify(new object?[] { null }.SequenceEqual(dataRow.Data)); + dataRow.Data.Should().BeEquivalentTo(new object?[] { null }); } public void ConstructorShouldSetMultipleDataValuesPassed() { var dataRow = new DataRowAttribute("mercury", "venus", "earth"); - Verify(new object[] { "mercury", "venus", "earth" }.SequenceEqual(dataRow.Data)); + dataRow.Data.Should().BeEquivalentTo(new object[] { "mercury", "venus", "earth" }); } public void ConstructorShouldSetANullDataValuePassedInParams() { var dataRow = new DataRowAttribute("neptune", null); - Verify(new object?[] { "neptune", null }.SequenceEqual(dataRow.Data)); + dataRow.Data.Should().BeEquivalentTo(new object?[] { "neptune", null }); } public void ConstructorShouldSetANullDataValuePassedInAsADataArg() { var dataRow = new DataRowAttribute(null, "logos"); - Verify(new object?[] { null, "logos" }.SequenceEqual(dataRow.Data)); + dataRow.Data.Should().BeEquivalentTo(new object?[] { null, "logos" }); } public void ConstructorShouldSetMultipleDataArrays() @@ -58,16 +60,16 @@ public void ConstructorShouldSetMultipleDataArrays() // Fixes https://github.com/microsoft/testfx/issues/1180 var dataRow = new DataRowAttribute(new[] { "a" }, new[] { "b" }); - Verify(dataRow.Data.Length == 2); - Verify(dataRow.Data[0] is string[] array1 && array1.SequenceEqual(["a"])); - Verify(dataRow.Data[1] is string[] array2 && array2.SequenceEqual(["b"])); + dataRow.Data.Should().HaveCount(2); + dataRow.Data[0].Should().BeOfType().Which.Should().BeEquivalentTo(["a"]); + dataRow.Data[1].Should().BeOfType().Which.Should().BeEquivalentTo(["b"]); } public void GetDataShouldReturnDataPassed() { var dataRow = new DataRowAttribute("mercury"); - Verify(new object[] { "mercury" }.SequenceEqual(dataRow.GetData(null!).Single())); + dataRow.GetData(null!).Single().Should().BeEquivalentTo(new object[] { "mercury" }); } public void GetDisplayNameShouldReturnAppropriateName() @@ -82,13 +84,13 @@ public void GetDisplayNameShouldReturnAppropriateName() string?[] data2 = ["First", null, "Second"]; string? displayName = dataRowAttribute.GetDisplayName(testMethodInfo, data); - Verify(displayName == "DataRowTestMethod (\"First\",\"Second\",null)"); + displayName.Should().Be("DataRowTestMethod (\"First\",\"Second\",null)"); displayName = dataRowAttribute.GetDisplayName(testMethodInfo, data1); - Verify(displayName == "DataRowTestMethod (null,\"First\",\"Second\")"); + displayName.Should().Be("DataRowTestMethod (null,\"First\",\"Second\")"); displayName = dataRowAttribute.GetDisplayName(testMethodInfo, data2); - Verify(displayName == "DataRowTestMethod (\"First\",null,\"Second\")"); + displayName.Should().Be("DataRowTestMethod (\"First\",null,\"Second\")"); } public void GetDisplayNameShouldReturnSpecifiedDisplayName() @@ -104,7 +106,7 @@ public void GetDisplayNameShouldReturnSpecifiedDisplayName() string?[] data = ["First", "Second", null]; string? displayName = dataRowAttribute.GetDisplayName(testMethodInfo, data); - Verify(displayName == "DataRowTestWithDisplayName"); + displayName.Should().Be("DataRowTestWithDisplayName"); } public void GetDisplayNameForArrayOfOneItem() @@ -118,7 +120,7 @@ public void GetDisplayNameForArrayOfOneItem() string? displayName = dataRow.GetDisplayName(methodInfoMock.Object, dataRow.Data); // Assert - Verify(displayName == "MyMethod ([\"a\"])"); + displayName.Should().Be("MyMethod ([\"a\"])"); } public void GetDisplayName_AfterOverriding_GetsTheNewDisplayName() @@ -131,7 +133,7 @@ public void GetDisplayName_AfterOverriding_GetsTheNewDisplayName() string? displayName = dataRow.GetDisplayName(methodInfoMock.Object, dataRow.Data); // Assert - Verify(displayName == "Overridden DisplayName"); + displayName.Should().Be("Overridden DisplayName"); } public void GetDisplayNameForArrayOfMultipleItems() @@ -145,7 +147,7 @@ public void GetDisplayNameForArrayOfMultipleItems() string? displayName = dataRow.GetDisplayName(methodInfoMock.Object, dataRow.Data); // Assert - Verify(displayName == "MyMethod ([\"a\",\"b\",\"c\"])"); + displayName.Should().Be("MyMethod ([\"a\",\"b\",\"c\"])"); } public void GetDisplayNameForMultipleArraysOfOneItem() @@ -159,7 +161,7 @@ public void GetDisplayNameForMultipleArraysOfOneItem() string? displayName = dataRow.GetDisplayName(methodInfoMock.Object, dataRow.Data); // Assert - Verify(displayName == "MyMethod ([\"a\"],[\"1\"])"); + displayName.Should().Be("MyMethod ([\"a\"],[\"1\"])"); } public void GetDisplayNameForMultipleArraysOfMultipleItems() @@ -173,7 +175,7 @@ public void GetDisplayNameForMultipleArraysOfMultipleItems() string? displayName = dataRow.GetDisplayName(methodInfoMock.Object, dataRow.Data); // Assert - Verify(displayName == "MyMethod ([\"a\",\"b\",\"c\"],[\"1\",\"2\",\"3\"])"); + displayName.Should().Be("MyMethod ([\"a\",\"b\",\"c\"],[\"1\",\"2\",\"3\"])"); } public void GetDisplayNameForMultipleArraysOfMultipleItemsValueTypes() @@ -187,7 +189,7 @@ public void GetDisplayNameForMultipleArraysOfMultipleItemsValueTypes() string? displayName = dataRow.GetDisplayName(methodInfoMock.Object, dataRow.Data); // Assert - Verify(displayName == "MyMethod ([1,2,3],[4,5,6])"); + displayName.Should().Be("MyMethod ([1,2,3],[4,5,6])"); } public void GetDisplayNameForMultipleArraysOfArraysOfMultipleItems() @@ -201,7 +203,7 @@ public void GetDisplayNameForMultipleArraysOfArraysOfMultipleItems() 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\"]])"); + displayName.Should().Be("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 diff --git a/test/UnitTests/TestFramework.UnitTests/Attributes/DynamicDataAttributeTests.cs b/test/UnitTests/TestFramework.UnitTests/Attributes/DynamicDataAttributeTests.cs new file mode 100644 index 0000000000..4edfc66c98 --- /dev/null +++ b/test/UnitTests/TestFramework.UnitTests/Attributes/DynamicDataAttributeTests.cs @@ -0,0 +1,663 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using AwesomeAssertions; + +using Microsoft.VisualStudio.TestTools.UnitTesting.Resources; + +using TestFramework.ForTestingMSTest; + +namespace Microsoft.VisualStudio.TestPlatform.TestFramework.UnitTests.Attributes; + +public class DynamicDataAttributeTests : TestContainer +{ + private readonly DummyTestClass _dummyTestClass; + private readonly MethodInfo _testMethodInfo; + private DynamicDataAttribute _dynamicDataAttribute; + + public DynamicDataAttributeTests() + { + _dummyTestClass = new DummyTestClass(); + _testMethodInfo = _dummyTestClass.GetType().GetTypeInfo().GetDeclaredMethod("TestMethod1")!; + _dynamicDataAttribute = new DynamicDataAttribute("ReusableTestDataProperty"); + + DynamicDataAttribute.TestIdGenerationStrategy = TestIdGenerationStrategy.FullyQualified; + } + + public void GetDataShouldThrowExceptionIfInvalidPropertyNameIsSpecifiedOrPropertyDoesNotExist() + { + _dynamicDataAttribute = new DynamicDataAttribute("ABC"); + Action action = () => _dynamicDataAttribute.GetData(_testMethodInfo); + action.Should().Throw() + .And.Message.Should().Be(string.Format(CultureInfo.InvariantCulture, FrameworkMessages.DynamicDataSourceShouldExistAndBeValid, "ABC", _testMethodInfo.DeclaringType!.FullName)); + } + + public void GetDataShouldReadDataFromProperty() + { + MethodInfo methodInfo = _dummyTestClass.GetType().GetTypeInfo().GetDeclaredMethod("TestMethod1")!; + _dynamicDataAttribute = new DynamicDataAttribute("ReusableTestDataProperty"); + IEnumerable data = _dynamicDataAttribute.GetData(methodInfo); + data.Should().NotBeNull(); + data.ToList().Should().HaveCount(2); + } + + public void GetDataShouldReadDataFromPropertyInDifferentClass() + { + MethodInfo methodInfo = _dummyTestClass.GetType().GetTypeInfo().GetDeclaredMethod("TestMethod1")!; + _dynamicDataAttribute = new DynamicDataAttribute("ReusableTestDataProperty2", typeof(DummyTestClass2)); + IEnumerable data = _dynamicDataAttribute.GetData(methodInfo); + data.Should().NotBeNull(); + data.ToList().Should().HaveCount(2); + } + + public void GetDataShouldReadDataFromMethod() + { + MethodInfo methodInfo = _dummyTestClass.GetType().GetTypeInfo().GetDeclaredMethod("TestMethod2")!; + _dynamicDataAttribute = new DynamicDataAttribute("ReusableTestDataMethod", DynamicDataSourceType.Method); + IEnumerable data = _dynamicDataAttribute.GetData(methodInfo); + data.Should().NotBeNull(); + data.ToList().Should().HaveCount(2); + } + + public void GetDataShouldReadDataFromMethodInDifferentClass() + { + MethodInfo methodInfo = _dummyTestClass.GetType().GetTypeInfo().GetDeclaredMethod("TestMethod2")!; + _dynamicDataAttribute = new DynamicDataAttribute("ReusableTestDataMethod2", typeof(DummyTestClass2), DynamicDataSourceType.Method); + IEnumerable data = _dynamicDataAttribute.GetData(methodInfo); + data.Should().NotBeNull(); + data.ToList().Should().HaveCount(2); + } + + public void GetDataShouldThrowExceptionIfPropertyReturnsNull() + { + Action action = () => + { + MethodInfo methodInfo = _dummyTestClass.GetType().GetTypeInfo().GetDeclaredMethod("TestMethod4")!; + _dynamicDataAttribute = new DynamicDataAttribute("NullProperty", typeof(DummyTestClass)); + _dynamicDataAttribute.GetData(methodInfo); + }; + action.Should().Throw(); + } + + public void GetDataShouldNotThrowExceptionIfPropertyReturnsEmpty() + { + MethodInfo methodInfo = _dummyTestClass.GetType().GetTypeInfo().GetDeclaredMethod("TestMethod5")!; + _dynamicDataAttribute = new DynamicDataAttribute("EmptyProperty", typeof(DummyTestClass)); + IEnumerable data = _dynamicDataAttribute.GetData(methodInfo); + // The callers in AssemblyEnumerator and TestMethodRunner are responsible + // for throwing an exception if data is empty and ConsiderEmptyDataSourceAsInconclusive is false. + data.Should().BeEmpty(); + } + + public void GetDataShouldThrowExceptionIfPropertyDoesNotReturnCorrectType() + { + Action action = () => + { + MethodInfo methodInfo = _dummyTestClass.GetType().GetTypeInfo().GetDeclaredMethod("TestMethod3")!; + _dynamicDataAttribute = new DynamicDataAttribute("WrongDataTypeProperty", typeof(DummyTestClass)); + _dynamicDataAttribute.GetData(methodInfo); + }; + action.Should().Throw(); + } + + public void GetDisplayNameShouldReturnDisplayName() + { + object[] data = [1, 2, 3]; + + string? displayName = _dynamicDataAttribute.GetDisplayName(_testMethodInfo, data); + displayName.Should().Be("TestMethod1 (1,2,3)"); + } + + public void GetDisplayNameShouldReturnDisplayNameWithDynamicDataDisplayName() + { + object[] data = [1, 2, 3]; + + _dynamicDataAttribute.DynamicDataDisplayName = "GetCustomDynamicDataDisplayName"; + string? displayName = _dynamicDataAttribute.GetDisplayName(_testMethodInfo, data); + displayName.Should().Be("DynamicDataTestWithDisplayName TestMethod1 with 3 parameters"); + } + + public void GetDisplayNameShouldReturnDisplayNameWithDynamicDataDisplayNameInDifferentClass() + { + object[] data = [1, 2, 3]; + + _dynamicDataAttribute.DynamicDataDisplayName = "GetCustomDynamicDataDisplayName2"; + _dynamicDataAttribute.DynamicDataDisplayNameDeclaringType = typeof(DummyTestClass2); + string? displayName = _dynamicDataAttribute.GetDisplayName(_testMethodInfo, data); + displayName.Should().Be("DynamicDataTestWithDisplayName TestMethod1 with 3 parameters"); + } + + public void GetDisplayNameShouldThrowExceptionWithDynamicDataDisplayNameMethodMissingParameters() + { + Action action = () => + { + object[] data = [1, 2, 3]; + + _dynamicDataAttribute.DynamicDataDisplayName = "GetDynamicDataDisplayNameWithMissingParameters"; + _dynamicDataAttribute.GetDisplayName(_testMethodInfo, data); + }; + action.Should().Throw(); + } + + public void GetDisplayNameShouldThrowExceptionWithDynamicDataDisplayNameMethodInvalidReturnType() + { + Action action = () => + { + object[] data = [1, 2, 3]; + + _dynamicDataAttribute.DynamicDataDisplayName = "GetDynamicDataDisplayNameWithInvalidReturnType"; + _dynamicDataAttribute.GetDisplayName(_testMethodInfo, data); + }; + action.Should().Throw(); + } + + public void GetDisplayNameShouldThrowExceptionWithDynamicDataDisplayNameMethodInvalidFirstParameterType() + { + Action action = () => + { + object[] data = [1, 2, 3]; + + _dynamicDataAttribute.DynamicDataDisplayName = "GetDynamicDataDisplayNameWithInvalidFirstParameterType"; + _dynamicDataAttribute.GetDisplayName(_testMethodInfo, data); + }; + action.Should().Throw(); + } + + public void GetDisplayNameShouldThrowExceptionWithDynamicDataDisplayNameMethodInvalidSecondParameterType() + { + Action action = () => + { + object[] data = [1, 2, 3]; + + _dynamicDataAttribute.DynamicDataDisplayName = "GetDynamicDataDisplayNameWithInvalidSecondParameterType"; + _dynamicDataAttribute.GetDisplayName(_testMethodInfo, data); + }; + action.Should().Throw(); + } + + public void GetDisplayNameShouldThrowExceptionWithDynamicDataDisplayNameMethodNonStatic() + { + Action action = () => + { + object[] data = [1, 2, 3]; + + _dynamicDataAttribute.DynamicDataDisplayName = "GetDynamicDataDisplayNameNonStatic"; + _dynamicDataAttribute.GetDisplayName(_testMethodInfo, data); + }; + action.Should().Throw(); + } + + public void GetDisplayNameShouldThrowExceptionWithDynamicDataDisplayNameMethodPrivate() + { + Action action = () => + { + object[] data = [1, 2, 3]; + + _dynamicDataAttribute.DynamicDataDisplayName = "GetDynamicDataDisplayNamePrivate"; + _dynamicDataAttribute.GetDisplayName(_testMethodInfo, data); + }; + action.Should().Throw(); + } + + public void GetDisplayNameShouldThrowExceptionWithMissingDynamicDataDisplayNameMethod() + { + Action action = () => + { + object[] data = [1, 2, 3]; + + _dynamicDataAttribute.DynamicDataDisplayName = "MissingCustomDynamicDataDisplayName"; + _dynamicDataAttribute.GetDisplayName(_testMethodInfo, data); + }; + action.Should().Throw(); + } + + public void GetDisplayNameShouldReturnEmptyStringIfDataIsNull() + { + string? displayName = _dynamicDataAttribute.GetDisplayName(_testMethodInfo, null); + displayName.Should().BeNull(); + } + + public void GetDisplayNameHandlesNullValues() + { + string?[] data = ["value1", "value2", null]; + string?[] data1 = [null, "value1", "value2"]; + string?[] data2 = ["value1", null, "value2"]; + + string? displayName = _dynamicDataAttribute.GetDisplayName(_testMethodInfo, data); + displayName.Should().Be("TestMethod1 (\"value1\",\"value2\",null)"); + + displayName = _dynamicDataAttribute.GetDisplayName(_testMethodInfo, data1); + displayName.Should().Be("TestMethod1 (null,\"value1\",\"value2\")"); + + displayName = _dynamicDataAttribute.GetDisplayName(_testMethodInfo, data2); + displayName.Should().Be("TestMethod1 (\"value1\",null,\"value2\")"); + } + + public void GetDisplayNameForArrayOfMultipleItems() + { + string? displayName = _dynamicDataAttribute.GetDisplayName(_testMethodInfo, [new[] { "a", "b", "c" }]); + displayName.Should().Be("TestMethod1 ([\"a\",\"b\",\"c\"])"); + } + + public void GetDisplayNameForMultipleArraysOfOneItem() + { + string? displayName = _dynamicDataAttribute.GetDisplayName(_testMethodInfo, [new[] { "a" }, new[] { "1" }]); + displayName.Should().Be("TestMethod1 ([\"a\"],[\"1\"])"); + } + + public void GetDisplayNameForMultipleArraysOfMultipleItems() + { + string? displayName = _dynamicDataAttribute.GetDisplayName(_testMethodInfo, [new[] { "a", "b", "c" }, new[] { "1", "2", "3" }]); + displayName.Should().Be("TestMethod1 ([\"a\",\"b\",\"c\"],[\"1\",\"2\",\"3\"])"); + } + + public void GetDisplayNameForMultipleArraysOfMultipleItemsValueTypes() + { + string? displayName = _dynamicDataAttribute.GetDisplayName(_testMethodInfo, [new[] { 1, 2, 3 }, new[] { 4, 5, 6 }]); + displayName.Should().Be("TestMethod1 ([1,2,3],[4,5,6])"); + } + + public void GetDisplayNameForMultipleArraysOfArraysOfMultipleItems() + { + string? displayName = _dynamicDataAttribute.GetDisplayName(_testMethodInfo, [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" } }]); + displayName.Should().Be("TestMethod1 ([[\"a\",\"b\",\"c\"],[\"d\",\"e\",\"f\"],[\"gh\",\"ij\",\"kl\"]],['m','n','o'],[[\"1\",\"2\",\"3\"],[\"4\",\"5\",\"6\"],[\"7\",\"8\",\"9\"]])"); + } + + public void DynamicDataSource_WithTuple_Works() + { + MethodInfo testMethodInfo = new TestClassTupleData().GetType().GetTypeInfo().GetDeclaredMethod(nameof(TestClassTupleData.DynamicDataTestWithTuple))!; + var dynamicDataAttribute = new DynamicDataAttribute(nameof(TestClassTupleData.DataWithTuple), typeof(TestClassTupleData)); + dynamicDataAttribute.GetData(testMethodInfo); + + dynamicDataAttribute = new DynamicDataAttribute(nameof(TestClassTupleData.GetDataWithTuple), typeof(TestClassTupleData), DynamicDataSourceType.Method); + 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)); + dynamicDataAttribute.GetData(testMethodInfo); + + dynamicDataAttribute = new DynamicDataAttribute(nameof(TestClassTupleData.GetDataWithValueTuple), typeof(TestClassTupleData), DynamicDataSourceType.Method); + 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)); + dynamicDataAttribute.GetData(testMethodInfo); + + dynamicDataAttribute = new DynamicDataAttribute(nameof(TestClassTupleData.GetDataWithValueTupleWithTupleSyntax), typeof(TestClassTupleData), DynamicDataSourceType.Method); + dynamicDataAttribute.GetData(testMethodInfo); + } + + public void GetDataShouldReadDataFromField() + { + MethodInfo methodInfo = _dummyTestClass.GetType().GetTypeInfo().GetDeclaredMethod("TestMethod1")!; + _dynamicDataAttribute = new DynamicDataAttribute("ReusableTestDataField", DynamicDataSourceType.Field); + IEnumerable data = _dynamicDataAttribute.GetData(methodInfo); + data.Should().NotBeNull(); + data.ToList().Should().HaveCount(2); + } + + public void GetDataShouldReadDataFromFieldInDifferentClass() + { + MethodInfo methodInfo = _dummyTestClass.GetType().GetTypeInfo().GetDeclaredMethod("TestMethod1")!; + _dynamicDataAttribute = new DynamicDataAttribute("ReusableTestDataField2", typeof(DummyTestClass2), DynamicDataSourceType.Field); + IEnumerable data = _dynamicDataAttribute.GetData(methodInfo); + data.Should().NotBeNull(); + data.ToList().Should().HaveCount(2); + } + + public void GetDataShouldReadDataFromFieldInAutoDetectMode() + { + MethodInfo methodInfo = _dummyTestClass.GetType().GetTypeInfo().GetDeclaredMethod("TestMethod1")!; + _dynamicDataAttribute = new DynamicDataAttribute("ReusableTestDataField"); + IEnumerable data = _dynamicDataAttribute.GetData(methodInfo); + data.Should().NotBeNull(); + data.ToList().Should().HaveCount(2); + } + + public void GetDataShouldThrowExceptionIfFieldReturnsNull() + { + Action action = () => + { + MethodInfo methodInfo = _dummyTestClass.GetType().GetTypeInfo().GetDeclaredMethod("TestMethod1")!; + _dynamicDataAttribute = new DynamicDataAttribute("NullField", typeof(DummyTestClass), DynamicDataSourceType.Field); + _dynamicDataAttribute.GetData(methodInfo); + }; + action.Should().Throw(); + } + + public void GetDataShouldNotThrowExceptionIfFieldReturnsEmpty() + { + MethodInfo methodInfo = _dummyTestClass.GetType().GetTypeInfo().GetDeclaredMethod("TestMethod1")!; + _dynamicDataAttribute = new DynamicDataAttribute("EmptyField", typeof(DummyTestClass), DynamicDataSourceType.Field); + IEnumerable data = _dynamicDataAttribute.GetData(methodInfo); + // The callers in AssemblyEnumerator and TestMethodRunner are responsible + // for throwing an exception if data is empty and ConsiderEmptyDataSourceAsInconclusive is false. + data.Should().BeEmpty(); + } + + public void GetDataShouldThrowExceptionIfFieldDoesNotReturnCorrectType() + { + Action action = () => + { + MethodInfo methodInfo = _dummyTestClass.GetType().GetTypeInfo().GetDeclaredMethod("TestMethod1")!; + _dynamicDataAttribute = new DynamicDataAttribute("WrongDataTypeField", typeof(DummyTestClass), DynamicDataSourceType.Field); + _dynamicDataAttribute.GetData(methodInfo); + }; + action.Should().Throw(); + } + + public void GetDataShouldThrowExceptionIfFieldIsNotStatic() + { + Action action = () => + { + MethodInfo methodInfo = _dummyTestClass.GetType().GetTypeInfo().GetDeclaredMethod("TestMethod1")!; + _dynamicDataAttribute = new DynamicDataAttribute("NonStaticField", typeof(DummyTestClass), DynamicDataSourceType.Field); + _dynamicDataAttribute.GetData(methodInfo); + }; + action.Should().Throw(); + } + + /// + /// The dummy test class. + /// + [TestClass] + [SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1401:Fields should be private", Justification = "Test use case")] + internal class DummyTestClass + { + /// + /// Gets the reusable test data property. + /// + public static IEnumerable ReusableTestDataProperty => [[1, 2, 3], [4, 5, 6]]; + + /// + /// Gets the null test data property. + /// + public static IEnumerable NullProperty => null!; + + /// + /// Gets the empty test data property. + /// + public static IEnumerable EmptyProperty => []; + + /// + /// Gets the wrong test data property i.e. Property returning something other than + /// expected data type of . + /// + public static string WrongDataTypeProperty => "Dummy"; + + /// + /// The reusable test data field. + /// + public static IEnumerable ReusableTestDataField = [[1, 2, 3], [4, 5, 6]]; + + /// + /// The null test data field. + /// + public static IEnumerable NullField = null!; + + /// + /// The empty test data field. + /// + public static IEnumerable EmptyField = []; + + /// + /// The wrong test data field i.e. Field returning something other than + /// expected data type of . + /// + public static string WrongDataTypeField = "Dummy"; + + /// + /// Non-static field that should cause an error. + /// + public IEnumerable NonStaticField = [[1, 2, 3]]; + + /// + /// The reusable test data method. + /// + /// + /// The . + /// + public static IEnumerable ReusableTestDataMethod() => [[1, 2, 3], [4, 5, 6]]; + + /// + /// The custom display name method. + /// + /// + /// The method info of test method. + /// + /// + /// The test data which is passed to test method. + /// + /// + /// The . + /// + public static string GetCustomDynamicDataDisplayName(MethodInfo methodInfo, object[] data) + => $"DynamicDataTestWithDisplayName {methodInfo.Name} with {data.Length} parameters"; + + /// + /// Custom display name method with missing parameters. + /// + /// + /// The . + /// + public static string GetDynamicDataDisplayNameWithMissingParameters() => throw new InvalidOperationException(); + + /// + /// Custom display name method with invalid return type. + /// + public static void GetDynamicDataDisplayNameWithInvalidReturnType() => throw new InvalidOperationException(); + + /// + /// Custom display name method with invalid first parameter type. + /// + /// + /// The method info of test method. + /// + /// + /// The test data which is passed to test method. + /// + /// + /// The . + /// + public static string GetDynamicDataDisplayNameWithInvalidFirstParameterType(string methodInfo, object[] data) => throw new InvalidOperationException(); + + /// + /// Custom display name method with invalid second parameter. + /// + /// + /// The method info of test method. + /// + /// + /// The test data which is passed to test method. + /// + /// + /// The . + /// + public static string GetDynamicDataDisplayNameWithInvalidSecondParameterType(MethodInfo methodInfo, string data) => throw new InvalidOperationException(); + + /// + /// Custom display name method that is not static. + /// + /// + /// The method info of test method. + /// + /// + /// The test data which is passed to test method. + /// + /// + /// The . + /// + public string GetDynamicDataDisplayNameNonStatic(MethodInfo methodInfo, object[] data) => throw new InvalidOperationException(); + + /// + /// The test method 1. + /// + [TestMethod] + [DynamicData("ReusableTestDataProperty")] + public void TestMethod1() + { + } + + /// + /// The test method 2. + /// + [TestMethod] + [DynamicData("ReusableTestDataMethod")] + public void TestMethod2() + { + } + + /// + /// The test method 3. + /// + [TestMethod] + [DynamicData("WrongDataTypeProperty")] + public void TestMethod3() + { + } + + /// + /// The test method 4. + /// + [TestMethod] + [DynamicData("NullProperty")] + public void TestMethod4() + { + } + + /// + /// The test method 5. + /// + [TestMethod] + [DynamicData("EmptyProperty")] + public void TestMethod5() + { + } + + /// + /// DataRow test method 1. + /// + [DataRow("First", "Second", null)] + [DataRow(null, "First", "Second")] + [DataRow("First", null, "Second")] + [TestMethod] + public void DataRowTestMethod() + { + } + + /// + /// Custom display name method that is private. + /// + /// + /// The method info of test method. + /// + /// + /// The test data which is passed to test method. + /// + /// + /// The . + /// +#pragma warning disable IDE0051 // Remove unused private members + private static string GetDynamicDataDisplayNamePrivate(MethodInfo methodInfo, object[] data) => throw new InvalidOperationException(); + } + + [SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1401:Fields should be private", Justification = "For testing the use case")] + [SuppressMessage("Usage", "CA2211:Non-constant fields should not be visible", Justification = "For testing the use case")] + public class DummyTestClass2 + { + /// + /// Gets the reusable test data property. + /// + public static IEnumerable ReusableTestDataProperty2 => [[1, 2, 3], [4, 5, 6]]; + + /// + /// The reusable test data method. + /// + /// + /// The . + /// + public static IEnumerable ReusableTestDataMethod2() => [[1, 2, 3], [4, 5, 6]]; + + /// + /// The reusable test data field. + /// + public static IEnumerable ReusableTestDataField2 = [[1, 2, 3], [4, 5, 6]]; + + /// + /// The custom display name method. + /// + /// + /// The method info of test method. + /// + /// + /// The test data which is passed to test method. + /// + /// + /// The . + /// + public static string GetCustomDynamicDataDisplayName2(MethodInfo methodInfo, object[] data) + => $"DynamicDataTestWithDisplayName {methodInfo.Name} with {data.Length} parameters"; + } + + [TestClass] + internal class TestClassTupleData + { + public static IEnumerable> GetDataWithTuple() + { + yield return new(0, "0"); + yield return new(1, "1"); + } + + public static IEnumerable> DataWithTuple + { + get + { + yield return new(0, "0"); + yield return new(1, "1"); + } + } + + [SuppressMessage("StyleCop.CSharp.ReadabilityRules", "SA1141:Use tuple syntax", Justification = "We want to explicitly test this syntax")] + public static IEnumerable> GetDataWithValueTuple() + { + yield return new(0, "0"); + yield return new(1, "1"); + } + + [SuppressMessage("StyleCop.CSharp.ReadabilityRules", "SA1141:Use tuple syntax", Justification = "We want to explicitly test this syntax")] + public static IEnumerable> DataWithValueTuple + { + get + { + yield return new(0, "0"); + yield return new(1, "1"); + } + } + + public static IEnumerable<(int Integer, string AsString)> GetDataWithValueTupleWithTupleSyntax() + { + yield return (0, "0"); + yield return (1, "1"); + } + + public static IEnumerable<(int Integer, string AsString)> DataWithValueTupleWithTupleSyntax + { + get + { + yield return (0, "0"); + yield return (1, "1"); + } + } + + [DataTestMethod] + public void DynamicDataTestWithTuple(int value, string integerAsString) + { + } + } +} diff --git a/test/UnitTests/TestFramework.UnitTests/Attributes/ExpectedExceptionAttributeTests.cs b/test/UnitTests/TestFramework.UnitTests/Attributes/ExpectedExceptionAttributeTests.cs index 30d558a0c7..dab8025581 100644 --- a/test/UnitTests/TestFramework.UnitTests/Attributes/ExpectedExceptionAttributeTests.cs +++ b/test/UnitTests/TestFramework.UnitTests/Attributes/ExpectedExceptionAttributeTests.cs @@ -1,6 +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 AwesomeAssertions; + using TestFramework.ForTestingMSTest; namespace UnitTestFramework.Tests; @@ -15,17 +17,19 @@ public class ExpectedExceptionAttributeTests : TestContainer /// public void ExpectedExceptionAttributeConstructorShouldThrowArgumentNullExceptionWhenExceptionTypeIsNull() { - static void A() => _ = new ExpectedExceptionAttribute(null!, "Dummy"); + Action action = () => _ = new ExpectedExceptionAttribute(null!, "Dummy"); - Exception ex = VerifyThrows(A); - Verify(ex is ArgumentNullException); + action.Should().Throw(); } /// /// ExpectedExceptionAttribute constructor should throw ArgumentNullException when parameter exceptionType = typeof(AnyClassNotDerivedFromExceptionClass). /// - public void ExpectedExceptionAttributeConstructerShouldThrowArgumentException() => - VerifyThrows(() => _ = new ExpectedExceptionAttribute(typeof(ExpectedExceptionAttributeTests), "Dummy")); + public void ExpectedExceptionAttributeConstructerShouldThrowArgumentException() + { + Action action = () => _ = new ExpectedExceptionAttribute(typeof(ExpectedExceptionAttributeTests), "Dummy"); + action.Should().Throw(); + } /// /// ExpectedExceptionAttribute constructor should not throw exception when parameter exceptionType = typeof(AnyClassDerivedFromExceptionClass). @@ -38,7 +42,7 @@ public void GetExceptionMsgShouldReturnExceptionMessage() Exception ex = new("Dummy Exception"); string actualMessage = UtfHelper.GetExceptionMsg(ex); string expectedMessage = "System.Exception: Dummy Exception"; - Verify(expectedMessage == actualMessage); + actualMessage.Should().Be(expectedMessage); } public void GetExceptionMsgShouldReturnInnerExceptionMessageAsWellIfPresent() @@ -47,7 +51,7 @@ public void GetExceptionMsgShouldReturnInnerExceptionMessageAsWellIfPresent() Exception ex = new("Dummy Exception", innerException); string actualMessage = UtfHelper.GetExceptionMsg(ex); string expectedMessage = "System.Exception: Dummy Exception ---> System.DivideByZeroException: Attempted to divide by zero."; - Verify(expectedMessage == actualMessage); + actualMessage.Should().Be(expectedMessage); } public void GetExceptionMsgShouldReturnInnerExceptionMessageRecursivelyIfPresent() @@ -57,7 +61,7 @@ public void GetExceptionMsgShouldReturnInnerExceptionMessageRecursivelyIfPresent Exception ex = new("FirstLevelException", innerException); string actualMessage = UtfHelper.GetExceptionMsg(ex); string expectedMessage = "System.Exception: FirstLevelException ---> System.DivideByZeroException: SecondLevel Exception ---> System.IndexOutOfRangeException: ThirdLevelException"; - Verify(expectedMessage == actualMessage); + actualMessage.Should().Be(expectedMessage); } } diff --git a/test/UnitTests/TestFramework.UnitTests/Attributes/ExpectedExceptionBaseAttributeTests.cs b/test/UnitTests/TestFramework.UnitTests/Attributes/ExpectedExceptionBaseAttributeTests.cs index 73da72f21a..6f85d99b5a 100644 --- a/test/UnitTests/TestFramework.UnitTests/Attributes/ExpectedExceptionBaseAttributeTests.cs +++ b/test/UnitTests/TestFramework.UnitTests/Attributes/ExpectedExceptionBaseAttributeTests.cs @@ -1,6 +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 AwesomeAssertions; + using TestFramework.ForTestingMSTest; namespace UnitTestFramework.Tests; @@ -19,17 +21,18 @@ public class ExpectedExceptionBaseAttributeTests : TestContainer /// public void RethrowIfAssertExceptionThrowsExceptionOnAssertFailure() { - void A() => _sut.RethrowIfAssertException(new AssertFailedException()); - - Exception ex = VerifyThrows(A); - Verify(ex is AssertFailedException); + Action action = () => _sut.RethrowIfAssertException(new AssertFailedException()); + action.Should().Throw(); } /// /// RethrowIfAssertException function will throw AssertFailedException if we pass AssertInconclusiveException as parameter in it. /// - public void RethrowIfAssertExceptionThrowsExceptionOnAssertInconclusive() => - VerifyThrows(() => _sut.RethrowIfAssertException(new AssertInconclusiveException())); + public void RethrowIfAssertExceptionThrowsExceptionOnAssertInconclusive() + { + Action action = () => _sut.RethrowIfAssertException(new AssertInconclusiveException()); + action.Should().Throw(); + } public void VerifyCorrectMessageIsGettingSetInVariableNoExceptionMessage() { @@ -38,7 +41,7 @@ public void VerifyCorrectMessageIsGettingSetInVariableNoExceptionMessage() string result = _sut.GetNoExceptionMessage(); - Verify(expected == result); + result.Should().Be(expected); } public void VerifyEmptyMessageIsGettingSetInVariableNoExceptionMessage() @@ -47,7 +50,7 @@ public void VerifyEmptyMessageIsGettingSetInVariableNoExceptionMessage() string result = _sut.GetNoExceptionMessage(); - Verify(string.IsNullOrEmpty(result)); + result.Should().BeNullOrEmpty(); } } diff --git a/test/UnitTests/TestFramework.UnitTests/GitHubWorkItemTests.cs b/test/UnitTests/TestFramework.UnitTests/GitHubWorkItemTests.cs index 35d607797c..39a5621502 100644 --- a/test/UnitTests/TestFramework.UnitTests/GitHubWorkItemTests.cs +++ b/test/UnitTests/TestFramework.UnitTests/GitHubWorkItemTests.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 AwesomeAssertions; + using TestFramework.ForTestingMSTest; namespace Microsoft.VisualStudio.TestPlatform.TestFramework.UnitTests; @@ -11,71 +13,71 @@ public void GitHubWorkItemAttributeShouldExtractIdFromUrl_IssueUrl() { string url = "https://github.com/microsoft/testfx/issues/1234"; GitHubWorkItemAttribute attribute = new(url); - Verify(attribute.Url == url); - Verify(attribute.Id == 1234); + attribute.Url.Should().Be(url); + attribute.Id.Should().Be(1234); } public void GitHubWorkItemAttributeShouldExtractIdFromUrl_IssueUrlWithEndingSlash() { string url = "https://github.com/microsoft/testfx/issues/1234/"; GitHubWorkItemAttribute attribute = new(url); - Verify(attribute.Url == url); - Verify(attribute.Id == 1234); + attribute.Url.Should().Be(url); + attribute.Id.Should().Be(1234); } public void GitHubWorkItemAttributeShouldExtractIdFromUrl_IssueUrlWithComment() { string url = "https://github.com/microsoft/testfx/issues/1234#issuecomment-2581012838"; GitHubWorkItemAttribute attribute = new(url); - Verify(attribute.Url == url); - Verify(attribute.Id == 1234); + attribute.Url.Should().Be(url); + attribute.Id.Should().Be(1234); } public void GitHubWorkItemAttributeShouldExtractIdFromUrl_PRUrl() { string url = "https://github.com/microsoft/testfx/pull/1234"; GitHubWorkItemAttribute attribute = new(url); - Verify(attribute.Url == url); - Verify(attribute.Id == 1234); + attribute.Url.Should().Be(url); + attribute.Id.Should().Be(1234); } public void GitHubWorkItemAttributeShouldExtractIdFromUrl_PRUrlWithEndingSlash() { string url = "https://github.com/microsoft/testfx/pull/1234/"; GitHubWorkItemAttribute attribute = new(url); - Verify(attribute.Url == url); - Verify(attribute.Id == 1234); + attribute.Url.Should().Be(url); + attribute.Id.Should().Be(1234); } public void GitHubWorkItemAttributeShouldExtractIdFromUrl_PRUrlWithComment() { string url = "https://github.com/microsoft/testfx/pull/1234#discussion_r1932733213"; GitHubWorkItemAttribute attribute = new(url); - Verify(attribute.Url == url); - Verify(attribute.Id == 1234); + attribute.Url.Should().Be(url); + attribute.Id.Should().Be(1234); } public void GitHubWorkItemAttributeShouldExtractIdFromUrl_DiscussionUrl() { string url = "https://github.com/microsoft/testfx/discussions/1234"; GitHubWorkItemAttribute attribute = new(url); - Verify(attribute.Url == url); - Verify(attribute.Id == 1234); + attribute.Url.Should().Be(url); + attribute.Id.Should().Be(1234); } public void GitHubWorkItemAttributeShouldExtractIdFromUrl_DiscussionUrlWithEndingSlash() { string url = "https://github.com/microsoft/testfx/discussions/1234/"; GitHubWorkItemAttribute attribute = new(url); - Verify(attribute.Url == url); - Verify(attribute.Id == 1234); + attribute.Url.Should().Be(url); + attribute.Id.Should().Be(1234); } public void GitHubWorkItemAttributeShouldExtractIdFromUrl_DiscussionUrlWithComment() { string url = "https://github.com/microsoft/testfx/discussions/1234#discussioncomment-11865020"; GitHubWorkItemAttribute attribute = new(url); - Verify(attribute.Url == url); - Verify(attribute.Id == 1234); + attribute.Url.Should().Be(url); + attribute.Id.Should().Be(1234); } } diff --git a/test/UnitTests/TestFramework.UnitTests/LoggerTests.cs b/test/UnitTests/TestFramework.UnitTests/LoggerTests.cs index b0e62e3c04..8ad336959b 100644 --- a/test/UnitTests/TestFramework.UnitTests/LoggerTests.cs +++ b/test/UnitTests/TestFramework.UnitTests/LoggerTests.cs @@ -1,6 +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 AwesomeAssertions; + using Microsoft.VisualStudio.TestTools.UnitTesting.Logging; using TestFramework.ForTestingMSTest; @@ -12,15 +14,15 @@ public sealed class LoggerTests : TestContainer public void LogMessageWhenFormatIsNullShouldThrow() { Logger.OnLogMessage += message => { }; - ArgumentNullException ex = VerifyThrows(() => Logger.LogMessage(null!, "arg1")); - Verify(ex.Message.Contains("format")); + Action act = () => Logger.LogMessage(null!, "arg1"); + act.Should().Throw().WithMessage("*format*"); } public void LogMessageWhenArgsIsNullShouldThrow() { Logger.OnLogMessage += message => { }; - ArgumentNullException ex = VerifyThrows(() => Logger.LogMessage("foo", null!)); - Verify(ex.Message.Contains("args")); + Action act = () => Logger.LogMessage("foo", null!); + act.Should().Throw().WithMessage("*args*"); } public void LogMessageWhenFormatIsSimpleMessageAndNoArgsShouldCallEvent() @@ -28,7 +30,7 @@ public void LogMessageWhenFormatIsSimpleMessageAndNoArgsShouldCallEvent() string? calledWith = null; Logger.OnLogMessage += message => calledWith = message; Logger.LogMessage("message"); - Verify(calledWith == "message"); + calledWith.Should().Be("message"); } public void LogMessageWhenFormatIsFormateMessageWithArgsShouldCallEvent() @@ -36,7 +38,7 @@ public void LogMessageWhenFormatIsFormateMessageWithArgsShouldCallEvent() string? calledWith = null; Logger.OnLogMessage += message => calledWith = message; Logger.LogMessage("message {0}", 1); - Verify(calledWith == "message 1"); + calledWith.Should().Be("message 1"); } public void LogMessageWhenFormatContainsCurlyBrace() @@ -44,6 +46,6 @@ public void LogMessageWhenFormatContainsCurlyBrace() string? calledWith = null; Logger.OnLogMessage += message => calledWith = message; Logger.LogMessage("{ A"); - Verify(calledWith == "{ A"); + calledWith.Should().Be("{ A"); } } diff --git a/test/UnitTests/TestFramework.UnitTests/TestDataRowTests.cs b/test/UnitTests/TestFramework.UnitTests/TestDataRowTests.cs new file mode 100644 index 0000000000..e2a6ea8dd2 --- /dev/null +++ b/test/UnitTests/TestFramework.UnitTests/TestDataRowTests.cs @@ -0,0 +1,81 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using AwesomeAssertions; + +using TestFramework.ForTestingMSTest; + +namespace Microsoft.VisualStudio.TestPlatform.TestFramework.UnitTests; + +public class TestDataRowTests : TestContainer +{ + public void TestDataRowShouldInitializeWithValue() + { + string value = "test_value"; + var testDataRow = new TestDataRow(value); + + testDataRow.Value.Should().Be(value); + testDataRow.IgnoreMessage.Should().BeNull(); + testDataRow.DisplayName.Should().BeNull(); + testDataRow.TestCategories.Should().BeNull(); + } + + public void TestDataRowShouldAllowSettingTestCategories() + { + string value = "test_value"; + var testDataRow = new TestDataRow(value); + var testCategories = new List { "Category1", "Category2" }; + + testDataRow.TestCategories = testCategories; + + testDataRow.TestCategories.Should().BeSameAs(testCategories); + testDataRow.TestCategories.Should().HaveCount(2); + testDataRow.TestCategories.Should().Contain("Category1"); + testDataRow.TestCategories.Should().Contain("Category2"); + } + + public void TestDataRowShouldImplementITestDataRowForTestCategories() + { + string value = "test_value"; + var testDataRow = new TestDataRow(value); + var testCategories = new List { "Integration", "Unit" }; + testDataRow.TestCategories = testCategories; + + ITestDataRow dataRow = testDataRow; + + dataRow.TestCategories.Should().NotBeNull(); + dataRow.TestCategories.Should().HaveCount(2); + dataRow.TestCategories.Should().Contain("Integration"); + dataRow.TestCategories.Should().Contain("Unit"); + } + + public void TestDataRowShouldAllowNullTestCategories() + { + string value = "test_value"; + var testDataRow = new TestDataRow(value) + { + TestCategories = null, + }; + + testDataRow.TestCategories.Should().BeNull(); + + ITestDataRow dataRow = testDataRow; + dataRow.TestCategories.Should().BeNull(); + } + + public void TestDataRowShouldAllowEmptyTestCategories() + { + string value = "test_value"; + var testDataRow = new TestDataRow(value); + var emptyCategories = new List(); + + testDataRow.TestCategories = emptyCategories; + + testDataRow.TestCategories.Should().BeSameAs(emptyCategories); + testDataRow.TestCategories.Should().HaveCount(0); + + ITestDataRow dataRow = testDataRow; + dataRow.TestCategories.Should().NotBeNull(); + dataRow.TestCategories.Should().HaveCount(0); + } +} diff --git a/test/UnitTests/TestFramework.UnitTests/TestResultTests.cs b/test/UnitTests/TestFramework.UnitTests/TestResultTests.cs index 99a9c47079..9dff5b841d 100644 --- a/test/UnitTests/TestFramework.UnitTests/TestResultTests.cs +++ b/test/UnitTests/TestFramework.UnitTests/TestResultTests.cs @@ -1,6 +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 AwesomeAssertions; + using TestFramework.ForTestingMSTest; namespace Microsoft.VisualStudio.TestPlatform.TestFramework.UnitTests; @@ -18,20 +20,20 @@ public void SettingTestFailureExceptionShouldAggregatePreviouslySetExceptions() }; // We use GetType() == typeof(...) to do a strict type match. - Verify(testResult.TestFailureException.GetType() == typeof(InvalidOperationException)); - Verify(testResult.TestFailureException.Message == "Failure1"); + testResult.TestFailureException.Should().BeOfType(); + testResult.TestFailureException.Message.Should().Be("Failure1"); testResult.TestFailureException = new ArgumentException("Failure2"); var aggregateException = (AggregateException)testResult.TestFailureException; - Verify(aggregateException.InnerExceptions.Count == 2); - Verify(aggregateException.InnerExceptions[0].Message == "Failure1"); - Verify(aggregateException.InnerExceptions[1].Message == "Failure2"); + aggregateException.InnerExceptions.Should().HaveCount(2); + aggregateException.InnerExceptions[0].Message.Should().Be("Failure1"); + aggregateException.InnerExceptions[1].Message.Should().Be("Failure2"); testResult.TestFailureException = new ArgumentException("Failure3"); aggregateException = (AggregateException)testResult.TestFailureException; - Verify(aggregateException.InnerExceptions.Count == 3); - Verify(aggregateException.InnerExceptions[0].Message == "Failure1"); - Verify(aggregateException.InnerExceptions[1].Message == "Failure2"); - Verify(aggregateException.InnerExceptions[2].Message == "Failure3"); + aggregateException.InnerExceptions.Should().HaveCount(3); + aggregateException.InnerExceptions[0].Message.Should().Be("Failure1"); + aggregateException.InnerExceptions[1].Message.Should().Be("Failure2"); + aggregateException.InnerExceptions[2].Message.Should().Be("Failure3"); } } diff --git a/test/Utilities/Automation.CLI/Automation.CLI.csproj b/test/Utilities/Automation.CLI/Automation.CLI.csproj index f37826a486..6b62895628 100644 --- a/test/Utilities/Automation.CLI/Automation.CLI.csproj +++ b/test/Utilities/Automation.CLI/Automation.CLI.csproj @@ -1,19 +1,17 @@ - + $(NetFrameworkMinimum) - - - + diff --git a/test/Utilities/Automation.CLI/CLITestBase.common.cs b/test/Utilities/Automation.CLI/CLITestBase.common.cs index 32762b329b..8a59fa515c 100644 --- a/test/Utilities/Automation.CLI/CLITestBase.common.cs +++ b/test/Utilities/Automation.CLI/CLITestBase.common.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 FluentAssertions; - -using TestFramework.ForTestingMSTest; +using Assert = Microsoft.VisualStudio.TestTools.UnitTesting.Assert; namespace Microsoft.MSTestV2.CLIAutomation; -public partial class CLITestBase : TestContainer +public abstract partial class CLITestBase { private const string Configuration = #if DEBUG @@ -45,7 +43,7 @@ protected static string GetArtifactsBinFolderPath() string assemblyLocation = Assembly.GetExecutingAssembly().Location; string artifactsBinFolder = Path.GetFullPath(Path.Combine(assemblyLocation, @"..\..\..\..")); - Directory.Exists(artifactsBinFolder).Should().BeTrue(); + Assert.IsTrue(Directory.Exists(artifactsBinFolder)); return artifactsBinFolder; } @@ -55,7 +53,7 @@ protected static string GetArtifactsTestResultsFolderPath() string assemblyLocation = Assembly.GetExecutingAssembly().Location; string artifactsFolder = Path.GetFullPath(Path.Combine(assemblyLocation, @"..\..\..\..\..")); - Directory.Exists(artifactsFolder).Should().BeTrue(); + Assert.IsTrue(Directory.Exists(artifactsFolder)); string testResultsFolder = Path.Combine(artifactsFolder, "TestResults", Configuration); Directory.CreateDirectory(testResultsFolder); @@ -68,7 +66,7 @@ protected static string GetAssetFullPath(string assetName, string? configuration configuration ??= Configuration; targetFramework ??= DefaultTargetFramework; string assetPath = Path.GetFullPath(Path.Combine(GetArtifactsBinFolderPath(), assetName, configuration, targetFramework, assetName + ".dll")); - File.Exists(assetPath).Should().BeTrue($"asset '{assetPath}' should exist"); + Assert.IsTrue(File.Exists(assetPath), $"asset '{assetPath}' should exist"); return assetPath; } diff --git a/test/Utilities/Automation.CLI/CLITestBase.e2e.cs b/test/Utilities/Automation.CLI/CLITestBase.e2e.cs index f49ad93ef1..76d01fb92b 100644 --- a/test/Utilities/Automation.CLI/CLITestBase.e2e.cs +++ b/test/Utilities/Automation.CLI/CLITestBase.e2e.cs @@ -1,21 +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 FluentAssertions; - using Microsoft.TestPlatform.VsTestConsole.TranslationLayer; using Microsoft.VisualStudio.TestPlatform.ObjectModel.Client; - -using TestFramework.ForTestingMSTest; +using Microsoft.VisualStudio.TestTools.UnitTesting; namespace Microsoft.MSTestV2.CLIAutomation; -public partial class CLITestBase : TestContainer +public abstract partial class CLITestBase { private static VsTestConsoleWrapper? s_vsTestConsoleWrapper; private DiscoveryEventsHandler? _discoveryEventsHandler; - public CLITestBase() + protected CLITestBase() { s_vsTestConsoleWrapper = new( GetConsoleRunnerPath(), @@ -73,14 +70,14 @@ public static string GetNugetPackageFolder() string nugetPackagesFolderPath = Environment.GetEnvironmentVariable("NUGET_PACKAGES"); if (!string.IsNullOrEmpty(nugetPackagesFolderPath)) { - Directory.Exists(nugetPackagesFolderPath).Should().BeTrue($"Found environment variable 'NUGET_PACKAGES' and NuGet package folder '{nugetPackagesFolderPath}' should exist"); + Assert.IsTrue(Directory.Exists(nugetPackagesFolderPath), $"Found environment variable 'NUGET_PACKAGES' and NuGet package folder '{nugetPackagesFolderPath}' should exist"); return nugetPackagesFolderPath; } string userProfile = Environment.GetEnvironmentVariable("USERPROFILE"); nugetPackagesFolderPath = Path.Combine(userProfile, ".nuget", "packages"); - Directory.Exists(nugetPackagesFolderPath).Should().BeTrue($"NuGet package folder '{nugetPackagesFolderPath}' should exist"); + Assert.IsTrue(Directory.Exists(nugetPackagesFolderPath), $"NuGet package folder '{nugetPackagesFolderPath}' should exist"); return nugetPackagesFolderPath; } @@ -122,13 +119,11 @@ public void ValidateDiscoveredTests(params string[] discoveredTestsList) { foreach (string test in discoveredTestsList) { - bool flag = _discoveryEventsHandler!.Tests.Contains(test) - || _discoveryEventsHandler.Tests.Contains(GetTestMethodName(test)); - flag.Should().BeTrue("Test '{0}' does not appear in discovered tests list.", test); + Assert.IsTrue(_discoveryEventsHandler!.Tests.Contains(test) || _discoveryEventsHandler.Tests.Contains(GetTestMethodName(test)), $"Test '{test}' does not appear in discovered tests list."); } // Make sure only expected number of tests are discovered and not more. - discoveredTestsList.Should().HaveSameCount(_discoveryEventsHandler!.Tests); + Assert.AreEqual(_discoveryEventsHandler!.Tests.Length, discoveredTestsList.Length); } /// @@ -144,7 +139,7 @@ public void ValidatePassedTests(params string[] passedTests) public void ValidatePassedTestsCount(int expectedPassedTestsCount) => // Make sure only expected number of tests passed and not more. - RunEventsHandler.PassedTests.Should().HaveCount(expectedPassedTestsCount); + Assert.HasCount(expectedPassedTestsCount, RunEventsHandler.PassedTests); /// /// Validates if the test results have the specified set of failed tests. @@ -181,7 +176,7 @@ public void ValidateFailedTests(bool validateStackTraceInfo, params string[] fai /// Expected failed tests count. public void ValidateFailedTestsCount(int expectedFailedTestsCount) => // Make sure only expected number of tests failed and not more. - RunEventsHandler.FailedTests.Should().HaveCount(expectedFailedTestsCount); + Assert.HasCount(expectedFailedTestsCount, RunEventsHandler.FailedTests); /// /// Validates if the test results have the specified set of skipped tests. @@ -191,8 +186,7 @@ public void ValidateFailedTestsCount(int expectedFailedTestsCount) => public void ValidateSkippedTests(params string[] skippedTests) { // Make sure only expected number of tests skipped and not more. - RunEventsHandler.SkippedTests.Should().HaveSameCount(skippedTests); - + Assert.AreEqual(skippedTests.Length, RunEventsHandler.SkippedTests.Count); ValidateSkippedTestsContain(skippedTests); } @@ -222,10 +216,11 @@ public void ValidatePassedTestsContain(params string[] passedTests) string failedOrSkippedMessage = isFailed ? " (Test failed)" : isSkipped ? " (Test skipped)" : string.Empty; - passedTestResults.Should().Contain( + Assert.Contains( p => test.Equals(p.TestCase.FullyQualifiedName, StringComparison.Ordinal) || test.Equals(p.DisplayName, StringComparison.Ordinal) || test.Equals(p.TestCase.DisplayName, StringComparison.Ordinal), + passedTestResults, $"Test '{test}' does not appear in passed tests list." + failedOrSkippedMessage); } } @@ -246,7 +241,7 @@ public void ValidateFailedTestsContain(bool validateStackTraceInfo, params strin { VisualStudio.TestPlatform.ObjectModel.TestResult testFound = RunEventsHandler.FailedTests.FirstOrDefault(f => test.Equals(f.TestCase?.FullyQualifiedName, StringComparison.Ordinal) || test.Equals(f.DisplayName, StringComparison.Ordinal)); - testFound.Should().NotBeNull("Test '{0}' does not appear in failed tests list.", test); + Assert.IsNotNull(testFound, "Test '{0}' does not appear in failed tests list.", test); #if DEBUG if (!validateStackTraceInfo) @@ -254,12 +249,15 @@ public void ValidateFailedTestsContain(bool validateStackTraceInfo, params strin continue; } - testFound.ErrorStackTrace.Should().NotBeNullOrWhiteSpace($"The test failure {testFound.DisplayName ?? testFound.TestCase.FullyQualifiedName} with message {testFound.ErrorMessage} lacks stack trace."); + Assert.IsFalse( + string.IsNullOrWhiteSpace(testFound.ErrorStackTrace), + $"The test failure {testFound.DisplayName ?? testFound.TestCase.FullyQualifiedName} with message {testFound.ErrorMessage} lacks stack trace."); // If test name is not empty, verify stack information as well. if (GetTestMethodName(test) is { Length: > 0 } testMethodName) { - testFound.ErrorStackTrace.Should().Contain(testMethodName, "No stack trace for failed test: {0}", test); + Assert.IsNotNull(testFound.ErrorStackTrace); + Assert.Contains(testMethodName, testFound.ErrorStackTrace, $"No stack trace for failed test: {test}"); } #endif } @@ -275,17 +273,17 @@ public void ValidateSkippedTestsContain(params string[] skippedTests) { foreach (string test in skippedTests) { - RunEventsHandler.SkippedTests.Should().Contain( + Assert.Contains( s => test.Equals(s.TestCase.FullyQualifiedName, StringComparison.Ordinal) || test.Equals(s.DisplayName, StringComparison.Ordinal), - "Test '{0}' does not appear in skipped tests list.", test); + RunEventsHandler.SkippedTests, + $"Test '{test}' does not appear in skipped tests list."); } } public void ValidateTestRunTime(int thresholdTime) - { - bool time = RunEventsHandler.ElapsedTimeInRunningTests >= 0 && RunEventsHandler.ElapsedTimeInRunningTests < thresholdTime; - time.Should().BeTrue($"Test Run was expected to not exceed {thresholdTime} but it took {RunEventsHandler.ElapsedTimeInRunningTests}"); - } + => Assert.IsTrue( + RunEventsHandler.ElapsedTimeInRunningTests >= 0 && RunEventsHandler.ElapsedTimeInRunningTests < thresholdTime, + $"Test Run was expected to not exceed {thresholdTime} but it took {RunEventsHandler.ElapsedTimeInRunningTests}"); /// /// Gets the test method name from full name. diff --git a/test/Utilities/Microsoft.Testing.TestInfrastructure/CommandLine.cs b/test/Utilities/Microsoft.Testing.TestInfrastructure/CommandLine.cs index 3fd02adb5b..51bf29848b 100644 --- a/test/Utilities/Microsoft.Testing.TestInfrastructure/CommandLine.cs +++ b/test/Utilities/Microsoft.Testing.TestInfrastructure/CommandLine.cs @@ -14,8 +14,8 @@ public sealed class CommandLine : IDisposable public static int TotalProcessesAttempt => s_totalProcessesAttempt; - private readonly List _errorOutputLines = new(); - private readonly List _standardOutputLines = new(); + private readonly List _errorOutputLines = []; + private readonly List _standardOutputLines = []; private IProcessHandle? _process; public ReadOnlyCollection StandardOutputLines => _standardOutputLines.AsReadOnly(); @@ -40,9 +40,10 @@ public static int MaxOutstandingCommands public async Task RunAsync( string commandLine, - IDictionary? environmentVariables = null) + IDictionary? environmentVariables = null, + CancellationToken cancellationToken = default) { - int exitCode = await RunAsyncAndReturnExitCodeAsync(commandLine, environmentVariables); + int exitCode = await RunAsyncAndReturnExitCodeAsync(commandLine, environmentVariables, cancellationToken: cancellationToken); if (exitCode != 0) { throw new InvalidOperationException( @@ -60,7 +61,7 @@ private static (string Command, string Arguments) GetCommandAndArguments(string if (!commandLine.StartsWith('"')) { string[] tokens = commandLine.Split(' '); - return (tokens[0], string.Join(" ", tokens.Skip(1))); + return (tokens[0], string.Join(' ', tokens.Skip(1))); } int endQuote = commandLine.IndexOf('"', 1); @@ -72,9 +73,9 @@ public async Task RunAsyncAndReturnExitCodeAsync( IDictionary? environmentVariables = null, string? workingDirectory = null, bool cleanDefaultEnvironmentVariableIfCustomAreProvided = false, - int timeoutInSeconds = 60) + CancellationToken cancellationToken = default) { - await s_maxOutstandingCommands_semaphore.WaitAsync(); + await s_maxOutstandingCommands_semaphore.WaitAsync(cancellationToken); try { Interlocked.Increment(ref s_totalProcessesAttempt); @@ -91,25 +92,8 @@ public async Task RunAsyncAndReturnExitCodeAsync( }; _process = ProcessFactory.Start(startInfo, cleanDefaultEnvironmentVariableIfCustomAreProvided); - Task exited = _process.WaitForExitAsync(); - int seconds = timeoutInSeconds; - CancellationTokenSource stopTheTimer = new(); - var timedOut = Task.Delay(TimeSpan.FromSeconds(seconds), stopTheTimer.Token); - if (await Task.WhenAny(exited, timedOut) == exited) - { - await stopTheTimer.CancelAsync(); - return await exited; - } - else - { - _process.Kill(); - throw new TimeoutException( - $""" - Timeout after {seconds}s on command line: '{commandLine}' - STD: {StandardOutput} - ERR: {ErrorOutput} - """); - } + using CancellationTokenRegistration registration = cancellationToken.Register(() => _process.Kill()); + return await _process.WaitForExitAsync(cancellationToken); } finally { diff --git a/test/Utilities/Microsoft.Testing.TestInfrastructure/DebuggerUtility.cs b/test/Utilities/Microsoft.Testing.TestInfrastructure/DebuggerUtility.cs index 2a7839e498..435e2f2d3d 100644 --- a/test/Utilities/Microsoft.Testing.TestInfrastructure/DebuggerUtility.cs +++ b/test/Utilities/Microsoft.Testing.TestInfrastructure/DebuggerUtility.cs @@ -125,7 +125,6 @@ private static bool AttachVs(Process vs, int pid, bool enableLog = false) if (dn.StartsWith("!VisualStudio.DTE.", StringComparison.Ordinal) && dn.EndsWith(dteSuffix, StringComparison.Ordinal)) { - object dbg, lps; runningObjectTable.GetObject(moniker[0], out object dte); // The COM object can be busy, we retry few times, hoping that it won't be busy next time. @@ -133,8 +132,8 @@ private static bool AttachVs(Process vs, int pid, bool enableLog = false) { try { - dbg = dte.GetType().InvokeMember("Debugger", BindingFlags.GetProperty, null, dte, null, CultureInfo.InvariantCulture)!; - lps = dbg.GetType().InvokeMember("LocalProcesses", BindingFlags.GetProperty, null, dbg, null, CultureInfo.InvariantCulture)!; + object dbg = dte.GetType().InvokeMember("Debugger", BindingFlags.GetProperty, null, dte, null, CultureInfo.InvariantCulture)!; + object lps = dbg.GetType().InvokeMember("LocalProcesses", BindingFlags.GetProperty, null, dbg, null, CultureInfo.InvariantCulture)!; var lpn = (System.Collections.IEnumerator)lps.GetType().InvokeMember("GetEnumerator", BindingFlags.InvokeMethod, null, lps, null, CultureInfo.InvariantCulture)!; while (lpn.MoveNext()) @@ -324,5 +323,5 @@ private static extern int NtQueryInformationProcess( out int returnLength); [DllImport("ole32.dll")] - private static extern int CreateBindCtx(uint reserved, out IBindCtx ppbc); + private static extern int CreateBindCtx(uint reserved, out IBindCtx? ppbc); } diff --git a/test/Utilities/Microsoft.Testing.TestInfrastructure/DotnetCli.cs b/test/Utilities/Microsoft.Testing.TestInfrastructure/DotnetCli.cs index 999006ce74..961b396e17 100644 --- a/test/Utilities/Microsoft.Testing.TestInfrastructure/DotnetCli.cs +++ b/test/Utilities/Microsoft.Testing.TestInfrastructure/DotnetCli.cs @@ -54,17 +54,17 @@ public static async Task RunAsync( Dictionary? environmentVariables = null, bool failIfReturnValueIsNotZero = true, bool disableTelemetry = true, - int timeoutInSeconds = 50, int retryCount = 5, bool disableCodeCoverage = true, bool warnAsError = true, bool suppressPreviewDotNetMessage = true, - [CallerMemberName] string callerMemberName = "") + [CallerMemberName] string callerMemberName = "", + CancellationToken cancellationToken = default) { - await s_maxOutstandingCommands_semaphore.WaitAsync(); + await s_maxOutstandingCommands_semaphore.WaitAsync(cancellationToken); try { - environmentVariables ??= new Dictionary(); + environmentVariables ??= []; foreach (DictionaryEntry entry in Environment.GetEnvironmentVariables()) { // Skip all unwanted environment variables. @@ -110,7 +110,7 @@ public static async Task RunAsync( if (DoNotRetry) { - return await CallTheMuxerAsync(args, environmentVariables, workingDirectory, timeoutInSeconds, failIfReturnValueIsNotZero, callerMemberName); + return await CallTheMuxerAsync(args, environmentVariables, workingDirectory, failIfReturnValueIsNotZero, callerMemberName, cancellationToken); } else { @@ -118,7 +118,7 @@ public static async Task RunAsync( return await Policy .Handle() .WaitAndRetryAsync(delay) - .ExecuteAsync(async () => await CallTheMuxerAsync(args, environmentVariables, workingDirectory, timeoutInSeconds, failIfReturnValueIsNotZero, callerMemberName)); + .ExecuteAsync(async ct => await CallTheMuxerAsync(args, environmentVariables, workingDirectory, failIfReturnValueIsNotZero, callerMemberName, ct), cancellationToken); } } finally @@ -131,13 +131,13 @@ private static bool IsDotNetTestWithExeOrDll(string args) => args.StartsWith("test ", StringComparison.Ordinal) && (args.Contains(".dll") || args.Contains(".exe")); // Workaround NuGet issue https://github.com/NuGet/Home/issues/14064 - private static async Task CallTheMuxerAsync(string args, Dictionary environmentVariables, string? workingDirectory, int timeoutInSeconds, bool failIfReturnValueIsNotZero, string binlogBaseFileName) + private static async Task CallTheMuxerAsync(string args, Dictionary environmentVariables, string? workingDirectory, bool failIfReturnValueIsNotZero, string binlogBaseFileName, CancellationToken cancellationToken) => await Policy .Handle(ex => ex.Message.Contains("MSB4236")) .WaitAndRetryAsync(retryCount: 3, sleepDurationProvider: static _ => TimeSpan.FromSeconds(2)) - .ExecuteAsync(async () => await CallTheMuxerCoreAsync(args, environmentVariables, workingDirectory, timeoutInSeconds, failIfReturnValueIsNotZero, binlogBaseFileName)); + .ExecuteAsync(async ct => await CallTheMuxerCoreAsync(args, environmentVariables, workingDirectory, failIfReturnValueIsNotZero, binlogBaseFileName, ct), cancellationToken); - private static async Task CallTheMuxerCoreAsync(string args, Dictionary environmentVariables, string? workingDirectory, int timeoutInSeconds, bool failIfReturnValueIsNotZero, string binlogBaseFileName) + private static async Task CallTheMuxerCoreAsync(string args, Dictionary environmentVariables, string? workingDirectory, bool failIfReturnValueIsNotZero, string binlogBaseFileName, CancellationToken cancellationToken) { if (args.StartsWith("dotnet ", StringComparison.OrdinalIgnoreCase)) { @@ -161,7 +161,7 @@ private static async Task CallTheMuxerCoreAsync(string args, } using DotnetMuxer dotnet = new(environmentVariables); - int exitCode = await dotnet.ExecuteAsync(args, workingDirectory, timeoutInSeconds); + int exitCode = await dotnet.ExecuteAsync(args, workingDirectory, cancellationToken); if (dotnet.StandardError.Contains("Invalid runtimeconfig.json")) { diff --git a/test/Utilities/Microsoft.Testing.TestInfrastructure/DotnetMuxer.cs b/test/Utilities/Microsoft.Testing.TestInfrastructure/DotnetMuxer.cs index 6be55cb242..d0dee689b9 100644 --- a/test/Utilities/Microsoft.Testing.TestInfrastructure/DotnetMuxer.cs +++ b/test/Utilities/Microsoft.Testing.TestInfrastructure/DotnetMuxer.cs @@ -92,20 +92,20 @@ protected virtual void Dispose(bool disposing) _isDisposed = true; } - public async Task ExecuteAsync(string arguments, string? workingDirectory = null, int timeoutInSeconds = 60) - => await ExecuteAsync(arguments, workingDirectory, _environmentVariables, timeoutInSeconds); + public async Task ExecuteAsync(string arguments, string? workingDirectory = null, CancellationToken cancellationToken = default) + => await ExecuteAsync(arguments, workingDirectory, _environmentVariables, cancellationToken); public async Task ExecuteAsync( string arguments, string? workingDirectory, IDictionary environmentVariables, - int timeoutInSeconds = 60) + CancellationToken cancellationToken) => await _commandLine.RunAsyncAndReturnExitCodeAsync( $"{_dotnet} {arguments}", environmentVariables, workingDirectory: workingDirectory, cleanDefaultEnvironmentVariableIfCustomAreProvided: true, - timeoutInSeconds: timeoutInSeconds); + cancellationToken); private static IDictionary MergeEnvironmentVariables( IDictionary environmentVariables1, diff --git a/test/Utilities/Microsoft.Testing.TestInfrastructure/IProcessHandle.cs b/test/Utilities/Microsoft.Testing.TestInfrastructure/IProcessHandle.cs index f9f3a43cc5..9307e6fdcc 100644 --- a/test/Utilities/Microsoft.Testing.TestInfrastructure/IProcessHandle.cs +++ b/test/Utilities/Microsoft.Testing.TestInfrastructure/IProcessHandle.cs @@ -19,9 +19,9 @@ public interface IProcessHandle void Kill(); - Task StopAsync(); + Task StopAsync(CancellationToken cancellationToken = default); - Task WaitForExitAsync(); + Task WaitForExitAsync(CancellationToken cancellationToken = default); Task WriteInputAsync(string input); } diff --git a/test/Utilities/Microsoft.Testing.TestInfrastructure/Microsoft.Testing.TestInfrastructure.csproj b/test/Utilities/Microsoft.Testing.TestInfrastructure/Microsoft.Testing.TestInfrastructure.csproj index 4fa39e0f86..5533e96445 100644 --- a/test/Utilities/Microsoft.Testing.TestInfrastructure/Microsoft.Testing.TestInfrastructure.csproj +++ b/test/Utilities/Microsoft.Testing.TestInfrastructure/Microsoft.Testing.TestInfrastructure.csproj @@ -4,6 +4,7 @@ $(MicrosoftTestingTargetFrameworks);netstandard2.0 enable $(DefineConstants);SKIP_INTERMEDIATE_TARGET_FRAMEWORKS + $(DefineConstants);ROOT_FINDER_PUBLIC diff --git a/test/Utilities/Microsoft.Testing.TestInfrastructure/ProcessHandle.cs b/test/Utilities/Microsoft.Testing.TestInfrastructure/ProcessHandle.cs index fb32b027b3..31d4fc729c 100644 --- a/test/Utilities/Microsoft.Testing.TestInfrastructure/ProcessHandle.cs +++ b/test/Utilities/Microsoft.Testing.TestInfrastructure/ProcessHandle.cs @@ -26,18 +26,18 @@ internal ProcessHandle(Process process, ProcessHandleInfo processHandleInfo) public int ExitCode => _process.ExitCode; - public async Task WaitForExitAsync() + public async Task WaitForExitAsync(CancellationToken cancellationToken = default) { if (_disposed) { return _exitCode; } - await _process.WaitForExitAsync(); + await _process.WaitForExitAsync(cancellationToken); return _process.ExitCode; } - public async Task StopAsync() + public async Task StopAsync(CancellationToken cancellationToken = default) { if (_disposed) { @@ -45,7 +45,7 @@ public async Task StopAsync() } KillSafe(_process); - return await WaitForExitAsync(); + return await WaitForExitAsync(cancellationToken); } public void Kill() diff --git a/test/Utilities/Microsoft.Testing.TestInfrastructure/ProjectSystem.cs b/test/Utilities/Microsoft.Testing.TestInfrastructure/ProjectSystem.cs index 91deff4f72..f19bde0388 100644 --- a/test/Utilities/Microsoft.Testing.TestInfrastructure/ProjectSystem.cs +++ b/test/Utilities/Microsoft.Testing.TestInfrastructure/ProjectSystem.cs @@ -46,7 +46,7 @@ public VSSolution(string? solutionFolder, string? solutionName) AddOrUpdateFileContent(_solutionFileName, MergeSolutionContent()); } - public ICollection Projects { get; } = new List(); + public ICollection Projects { get; } = []; public string SolutionFile { get; private set; } diff --git a/test/Utilities/Microsoft.Testing.TestInfrastructure/RetryHelper.cs b/test/Utilities/Microsoft.Testing.TestInfrastructure/RetryHelper.cs index a2adaf6262..821e5f5b9a 100644 --- a/test/Utilities/Microsoft.Testing.TestInfrastructure/RetryHelper.cs +++ b/test/Utilities/Microsoft.Testing.TestInfrastructure/RetryHelper.cs @@ -7,13 +7,13 @@ namespace Microsoft.Testing.TestInfrastructure; public class RetryHelper { - public static async Task RetryAsync(Func action, uint times, TimeSpan every, Func? predicate = null) - => await Policy.Handle(exception => predicate is null || predicate(exception)) + public static async Task RetryAsync(Func action, uint times, TimeSpan every) + => await Policy.Handle() .WaitAndRetryAsync((int)times, _ => every) .ExecuteAsync(action); - public static async Task RetryAsync(Func> action, uint times, TimeSpan every, Func? predicate = null) - => await Policy.Handle(exception => predicate is null || predicate(exception)) + public static async Task RetryAsync(Func> action, uint times, TimeSpan every) + => await Policy.Handle() .WaitAndRetryAsync((int)times, _ => every) .ExecuteAsync(action); } diff --git a/test/Utilities/Microsoft.Testing.TestInfrastructure/RootFinder.cs b/test/Utilities/Microsoft.Testing.TestInfrastructure/RootFinder.cs index 32f01c4489..404f930f6a 100644 --- a/test/Utilities/Microsoft.Testing.TestInfrastructure/RootFinder.cs +++ b/test/Utilities/Microsoft.Testing.TestInfrastructure/RootFinder.cs @@ -3,22 +3,51 @@ namespace Microsoft.Testing.TestInfrastructure; -public static class RootFinder +/// +/// Provides functionality to locate the root directory of a Git repository. +/// +/// The class is used to find the root directory of a Git repository by +/// searching for a ".git" directory or file starting from the application's base directory and moving up the directory +/// hierarchy. This is useful for applications that need to determine the root of a project or repository. +#if ROOT_FINDER_PUBLIC +public +#else +internal +#endif + static class RootFinder { + private static string? s_root; + + /// + /// Finds the root directory of a Git repository starting from the application's base directory. + /// + /// This method searches for a ".git" directory or file in the application's base directory and + /// its parent directories. If a Git repository is found, the path to its root directory is returned. If no Git + /// repository is found, an is thrown. + /// The path to the root directory of the Git repository, ending with a directory separator character. + /// Thrown if a Git repository is not found in the application's base directory or any of its parent directories. public static string Find() { + if (s_root != null) + { + return s_root; + } + string path = AppContext.BaseDirectory; - string dir = path; - while (Directory.GetDirectoryRoot(dir) != dir) + string currentDirectory = path; + string rootDriveDirectory = Directory.GetDirectoryRoot(currentDirectory); + while (rootDriveDirectory != currentDirectory) { - if (Directory.Exists(Path.Combine(dir, ".git"))) - { - return dir; - } - else + string gitPath = Path.Combine(currentDirectory, ".git"); + + // When working with git worktrees, the .git is a file not a folder + if (Directory.Exists(gitPath) || File.Exists(gitPath)) { - dir = Directory.GetParent(dir)!.ToString(); + s_root = currentDirectory + Path.DirectorySeparatorChar; + return s_root; } + + currentDirectory = Directory.GetParent(currentDirectory)!.ToString(); } throw new InvalidOperationException($"Could not find solution root, .git not found in {path} or any parent directory."); diff --git a/test/Utilities/Microsoft.Testing.TestInfrastructure/TargetFrameworks.cs b/test/Utilities/Microsoft.Testing.TestInfrastructure/TargetFrameworks.cs index 3d4b2ffb33..824e2f1676 100644 --- a/test/Utilities/Microsoft.Testing.TestInfrastructure/TargetFrameworks.cs +++ b/test/Utilities/Microsoft.Testing.TestInfrastructure/TargetFrameworks.cs @@ -24,12 +24,12 @@ public static class TargetFrameworks public static string[] All { get; } = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) - ? Net.Concat(NetFramework).ToArray() + ? [.. Net, .. NetFramework] : Net; public static IEnumerable AllForDynamicData { get; } = All.Select(tfm => new object[] { tfm }); public static string ToMSBuildTargetFrameworks(this string[] targetFrameworksEntries) - => string.Join(";", targetFrameworksEntries); + => string.Join(';', targetFrameworksEntries); } diff --git a/test/Utilities/Microsoft.Testing.TestInfrastructure/TempDirectory.cs b/test/Utilities/Microsoft.Testing.TestInfrastructure/TempDirectory.cs index 76dcfca4b6..285a53fe3a 100644 --- a/test/Utilities/Microsoft.Testing.TestInfrastructure/TempDirectory.cs +++ b/test/Utilities/Microsoft.Testing.TestInfrastructure/TempDirectory.cs @@ -131,7 +131,7 @@ public string[] CopyFile(params string[] filePaths) paths.Add(destination); } - return paths.ToArray(); + return [.. paths]; } /// diff --git a/test/Utilities/Microsoft.Testing.TestInfrastructure/TestAssetFixtureBase.cs b/test/Utilities/Microsoft.Testing.TestInfrastructure/TestAssetFixtureBase.cs index 343e98d000..5178272cca 100644 --- a/test/Utilities/Microsoft.Testing.TestInfrastructure/TestAssetFixtureBase.cs +++ b/test/Utilities/Microsoft.Testing.TestInfrastructure/TestAssetFixtureBase.cs @@ -5,12 +5,12 @@ namespace Microsoft.Testing.TestInfrastructure; public interface ITestAssetFixture : IDisposable { - Task InitializeAsync(); + Task InitializeAsync(CancellationToken cancellationToken); } public sealed class NopAssetFixture : ITestAssetFixture { - public Task InitializeAsync() => Task.CompletedTask; + public Task InitializeAsync(CancellationToken cancellationToken) => Task.CompletedTask; public void Dispose() { @@ -32,13 +32,13 @@ public string GetAssetPath(string assetID) ? throw new ArgumentNullException(nameof(assetID), $"Cannot find target path for test asset '{assetID}'") : testAsset.TargetAssetPath; - public async Task InitializeAsync() => + public async Task InitializeAsync(CancellationToken cancellationToken) => // Generate all projects into the same temporary base folder, but separate subdirectories, so we can reference one from other. #if NET await Parallel.ForEachAsync(GetAssetsToGenerate(), async (asset, _) => { TestAsset testAsset = await TestAsset.GenerateAssetAsync(asset.ID, asset.Code, _tempDirectory); - DotnetMuxerResult result = await DotnetCli.RunAsync($"build {testAsset.TargetAssetPath} -c Release", _nugetGlobalPackagesDirectory.Path, callerMemberName: asset.Name); + DotnetMuxerResult result = await DotnetCli.RunAsync($"build {testAsset.TargetAssetPath} -c Release", _nugetGlobalPackagesDirectory.Path, callerMemberName: asset.Name, cancellationToken: cancellationToken); testAsset.DotnetResult = result; _testAssets.TryAdd(asset.ID, testAsset); }); @@ -46,7 +46,7 @@ await Parallel.ForEachAsync(GetAssetsToGenerate(), async (asset, _) => await Task.WhenAll(GetAssetsToGenerate().Select(async asset => { TestAsset testAsset = await TestAsset.GenerateAssetAsync(asset.Name, asset.Code, _tempDirectory); - DotnetMuxerResult result = await DotnetCli.RunAsync($"build {testAsset.TargetAssetPath} -c Release", _nugetGlobalPackagesDirectory.Path, callerMemberName: asset.Name); + DotnetMuxerResult result = await DotnetCli.RunAsync($"build {testAsset.TargetAssetPath} -c Release", _nugetGlobalPackagesDirectory.Path, callerMemberName: asset.Name, cancellationToken: cancellationToken); testAsset.DotnetResult = result; _testAssets.TryAdd(asset.ID, testAsset); })); diff --git a/test/Utilities/Microsoft.Testing.TestInfrastructure/TestHost.cs b/test/Utilities/Microsoft.Testing.TestInfrastructure/TestHost.cs index 32352b750b..91f9c992db 100644 --- a/test/Utilities/Microsoft.Testing.TestInfrastructure/TestHost.cs +++ b/test/Utilities/Microsoft.Testing.TestInfrastructure/TestHost.cs @@ -41,9 +41,9 @@ public async Task ExecuteAsync( string? command = null, Dictionary? environmentVariables = null, bool disableTelemetry = true, - int timeoutSeconds = 60) + CancellationToken cancellationToken = default) { - await s_maxOutstandingExecutions_semaphore.WaitAsync(); + await s_maxOutstandingExecutions_semaphore.WaitAsync(cancellationToken); try { if (command?.StartsWith(_testHostModuleName, StringComparison.OrdinalIgnoreCase) ?? false) @@ -51,7 +51,7 @@ public async Task ExecuteAsync( throw new InvalidOperationException($"Command should not start with module name '{_testHostModuleName}'."); } - environmentVariables ??= new Dictionary(); + environmentVariables ??= []; if (disableTelemetry) { @@ -84,18 +84,22 @@ public async Task ExecuteAsync( return await Policy .Handle() .WaitAndRetryAsync(delay) - .ExecuteAsync(async () => - { - CommandLine commandLine = new(); - int exitCode = await commandLine.RunAsyncAndReturnExitCodeAsync( - $"{FullName} {finalArguments}", - environmentVariables: environmentVariables, - workingDirectory: null, - cleanDefaultEnvironmentVariableIfCustomAreProvided: true, - timeoutInSeconds: timeoutSeconds); - string fullCommand = command is not null ? $"{FullName} {command}" : FullName; - return new TestHostResult(fullCommand, exitCode, commandLine.StandardOutput, commandLine.StandardOutputLines, commandLine.ErrorOutput, commandLine.ErrorOutputLines); - }); + .ExecuteAsync( + async ct => + { + CommandLine commandLine = new(); + // Disable ANSI rendering so tests have easier time parsing the output. + // Disable progress so tests don't mix progress with overall progress, and with test process output. + int exitCode = await commandLine.RunAsyncAndReturnExitCodeAsync( + $"{FullName} --no-ansi --no-progress {finalArguments}", + environmentVariables: environmentVariables, + workingDirectory: null, + cleanDefaultEnvironmentVariableIfCustomAreProvided: true, + cancellationToken: ct); + string fullCommand = command is not null ? $"{FullName} {command}" : FullName; + return new TestHostResult(fullCommand, exitCode, commandLine.StandardOutput, commandLine.StandardOutputLines, commandLine.ErrorOutput, commandLine.ErrorOutputLines); + }, + cancellationToken); } finally { diff --git a/test/Utilities/TestFramework.ForTestingMSTest/TestFrameworkEngine.cs b/test/Utilities/TestFramework.ForTestingMSTest/TestFrameworkEngine.cs index 0f26913c36..922dd103ac 100644 --- a/test/Utilities/TestFramework.ForTestingMSTest/TestFrameworkEngine.cs +++ b/test/Utilities/TestFramework.ForTestingMSTest/TestFrameworkEngine.cs @@ -20,8 +20,7 @@ public TestFrameworkEngine(TestFrameworkExtension extension, ILoggerFactory logg _logger = loggerFactory.CreateLogger("InternalTestFrameworkEngine"); } - public Type[] DataTypesProduced { get; } - = new Type[1] { typeof(TestNodeUpdateMessage) }; + public Type[] DataTypesProduced { get; } = [typeof(TestNodeUpdateMessage)]; public string Uid => _extension.Uid; @@ -99,7 +98,7 @@ private async Task ExecuteTestNodeRunAsync(RunTestExecutionRequest request, IMes testContainerType.Name, publicMethod.Name, publicMethod.GetGenericArguments().Length, - publicMethod.GetParameters().Select(x => x.ParameterType.FullName!).ToArray(), + [.. publicMethod.GetParameters().Select(x => x.ParameterType.FullName!)], publicMethod.ReturnType.FullName!)); testNode.Properties.Add(new TrxFullyQualifiedTypeNameProperty(testContainerType.FullName!)); @@ -176,7 +175,7 @@ private async Task ExecuteTestNodeDiscoveryAsync(DiscoverTestExecutionRequest re testContainerType.Name, publicMethod.Name, publicMethod.GetGenericArguments().Length, - publicMethod.GetParameters().Select(x => x.ParameterType.FullName!).ToArray(), + [.. publicMethod.GetParameters().Select(x => x.ParameterType.FullName!)], publicMethod.ReturnType.FullName!)); await messageBus.PublishAsync(this, new TestNodeUpdateMessage(request.Session.SessionUid, testNode));