From 961a1e1c274a5364f75a0bcdf5bcfc419162e2da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Amaury=20Lev=C3=A9?= Date: Wed, 18 Dec 2024 08:06:25 +0100 Subject: [PATCH] Open source Retry and Hot Reload extensions (#4354) --- .editorconfig | 1 + LICENSE.PLATFORMTOOLS.txt | 110 ++++++ TestFx.sln | 15 +- eng/Analyzers.props | 1 - eng/stylecop.json | 11 - eng/stylecop.ruleset | 15 - eng/stylecop.test.ruleset | 20 - .../.editorconfig | 2 + .../BannedSymbols.txt | 10 + .../HotReloadExtensions.cs | 15 + .../HotReloadHandler.cs | 91 +++++ .../HotReloadTestHostTestFrameworkInvoker.cs | 68 ++++ ...rosoft.Testing.Extensions.HotReload.csproj | 61 +++ .../PACKAGE.md | 9 + .../PublicAPI/PublicAPI.Shipped.txt | 5 + .../PublicAPI/PublicAPI.Unshipped.txt | 1 + .../Resources/ExtensionResources.Designer.cs | 90 +++++ .../Resources/ExtensionResources.resx | 129 ++++++ .../Resources/xlf/ExtensionResources.cs.xlf | 22 ++ .../Resources/xlf/ExtensionResources.de.xlf | 22 ++ .../Resources/xlf/ExtensionResources.es.xlf | 22 ++ .../Resources/xlf/ExtensionResources.fr.xlf | 22 ++ .../Resources/xlf/ExtensionResources.it.xlf | 22 ++ .../Resources/xlf/ExtensionResources.ja.xlf | 22 ++ .../Resources/xlf/ExtensionResources.ko.xlf | 22 ++ .../Resources/xlf/ExtensionResources.pl.xlf | 22 ++ .../xlf/ExtensionResources.pt-BR.xlf | 22 ++ .../Resources/xlf/ExtensionResources.ru.xlf | 22 ++ .../Resources/xlf/ExtensionResources.tr.xlf | 22 ++ .../xlf/ExtensionResources.zh-Hans.xlf | 22 ++ .../xlf/ExtensionResources.zh-Hant.xlf | 22 ++ .../TestingPlatformBuilderHook.cs | 12 + ...crosoft.Testing.Extensions.HotReload.props | 3 + ...crosoft.Testing.Extensions.HotReload.props | 9 + ...crosoft.Testing.Extensions.HotReload.props | 3 + .../.editorconfig | 2 + .../BannedSymbols.txt | 10 + .../Microsoft.Testing.Extensions.Retry.csproj | 65 ++++ .../PACKAGE.md | 9 + .../PublicAPI/PublicAPI.Shipped.txt | 5 + .../PublicAPI/PublicAPI.Unshipped.txt | 1 + .../RandomId.cs | 44 +++ .../Resources/ExtensionResources.Designer.cs | 284 ++++++++++++++ .../Resources/ExtensionResources.resx | 204 ++++++++++ .../Resources/xlf/ExtensionResources.cs.xlf | 150 +++++++ .../Resources/xlf/ExtensionResources.de.xlf | 150 +++++++ .../Resources/xlf/ExtensionResources.es.xlf | 151 ++++++++ .../Resources/xlf/ExtensionResources.fr.xlf | 150 +++++++ .../Resources/xlf/ExtensionResources.it.xlf | 150 +++++++ .../Resources/xlf/ExtensionResources.ja.xlf | 150 +++++++ .../Resources/xlf/ExtensionResources.ko.xlf | 150 +++++++ .../Resources/xlf/ExtensionResources.pl.xlf | 150 +++++++ .../xlf/ExtensionResources.pt-BR.xlf | 150 +++++++ .../Resources/xlf/ExtensionResources.ru.xlf | 150 +++++++ .../Resources/xlf/ExtensionResources.tr.xlf | 150 +++++++ .../xlf/ExtensionResources.zh-Hans.xlf | 150 +++++++ .../xlf/ExtensionResources.zh-Hant.xlf | 150 +++++++ .../RetryCommandLineOptionsProvider.cs | 85 ++++ .../RetryDataConsumer.cs | 79 ++++ .../RetryExecutionFilterFactory.cs | 50 +++ .../RetryExtensions.cs | 38 ++ .../RetryFailedTestsPipeServer.cs | 75 ++++ .../RetryLifecycleCallbacks.cs | 89 +++++ .../RetryOrchestrator.cs | 348 +++++++++++++++++ .../Serializers/FailedTestRequest.cs | 29 ++ .../Serializers/GetListOfFailedTests.cs | 20 + .../GetListOfFailedTestsResponse.cs | 40 ++ .../Serializers/TotalTestsRunRequest.cs | 29 ++ .../TestingPlatformBuilderHook.cs | 12 + .../Microsoft.Testing.Extensions.Retry.props | 3 + .../Microsoft.Testing.Extensions.Retry.props | 9 + .../Microsoft.Testing.Extensions.Retry.props | 3 + .../NativeAotTests.cs | 1 - .../DiagnosticTests.cs | 3 +- .../ExecutionTests.cs | 6 +- .../HelpInfoTests.cs | 10 +- .../Helpers/AcceptanceTestBase.cs | 4 - .../MSBuild.KnownExtensionRegistration.cs | 9 +- .../MSBuildTests.ConfigurationFile.cs | 6 +- .../MSBuildTests.GenerateEntryPoint.cs | 4 +- .../MSBuildTests.Solution.cs | 3 +- .../Properties/launchSettings.json | 2 +- .../RetryFailedTestsTests.cs | 366 ++++++++++++++++++ .../TimeoutTests.cs | 3 +- .../TrxTests.cs | 3 +- ...rosoft.Testing.Extensions.UnitTests.csproj | 3 + .../RetryTests.cs | 115 ++++++ 87 files changed, 4904 insertions(+), 85 deletions(-) create mode 100644 LICENSE.PLATFORMTOOLS.txt delete mode 100644 eng/stylecop.json delete mode 100644 eng/stylecop.ruleset delete mode 100644 eng/stylecop.test.ruleset create mode 100644 src/Platform/Microsoft.Testing.Extensions.HotReload/.editorconfig create mode 100644 src/Platform/Microsoft.Testing.Extensions.HotReload/BannedSymbols.txt create mode 100644 src/Platform/Microsoft.Testing.Extensions.HotReload/HotReloadExtensions.cs create mode 100644 src/Platform/Microsoft.Testing.Extensions.HotReload/HotReloadHandler.cs create mode 100644 src/Platform/Microsoft.Testing.Extensions.HotReload/HotReloadTestHostTestFrameworkInvoker.cs create mode 100644 src/Platform/Microsoft.Testing.Extensions.HotReload/Microsoft.Testing.Extensions.HotReload.csproj create mode 100644 src/Platform/Microsoft.Testing.Extensions.HotReload/PACKAGE.md create mode 100644 src/Platform/Microsoft.Testing.Extensions.HotReload/PublicAPI/PublicAPI.Shipped.txt create mode 100644 src/Platform/Microsoft.Testing.Extensions.HotReload/PublicAPI/PublicAPI.Unshipped.txt create mode 100644 src/Platform/Microsoft.Testing.Extensions.HotReload/Resources/ExtensionResources.Designer.cs create mode 100644 src/Platform/Microsoft.Testing.Extensions.HotReload/Resources/ExtensionResources.resx create mode 100644 src/Platform/Microsoft.Testing.Extensions.HotReload/Resources/xlf/ExtensionResources.cs.xlf create mode 100644 src/Platform/Microsoft.Testing.Extensions.HotReload/Resources/xlf/ExtensionResources.de.xlf create mode 100644 src/Platform/Microsoft.Testing.Extensions.HotReload/Resources/xlf/ExtensionResources.es.xlf create mode 100644 src/Platform/Microsoft.Testing.Extensions.HotReload/Resources/xlf/ExtensionResources.fr.xlf create mode 100644 src/Platform/Microsoft.Testing.Extensions.HotReload/Resources/xlf/ExtensionResources.it.xlf create mode 100644 src/Platform/Microsoft.Testing.Extensions.HotReload/Resources/xlf/ExtensionResources.ja.xlf create mode 100644 src/Platform/Microsoft.Testing.Extensions.HotReload/Resources/xlf/ExtensionResources.ko.xlf create mode 100644 src/Platform/Microsoft.Testing.Extensions.HotReload/Resources/xlf/ExtensionResources.pl.xlf create mode 100644 src/Platform/Microsoft.Testing.Extensions.HotReload/Resources/xlf/ExtensionResources.pt-BR.xlf create mode 100644 src/Platform/Microsoft.Testing.Extensions.HotReload/Resources/xlf/ExtensionResources.ru.xlf create mode 100644 src/Platform/Microsoft.Testing.Extensions.HotReload/Resources/xlf/ExtensionResources.tr.xlf create mode 100644 src/Platform/Microsoft.Testing.Extensions.HotReload/Resources/xlf/ExtensionResources.zh-Hans.xlf create mode 100644 src/Platform/Microsoft.Testing.Extensions.HotReload/Resources/xlf/ExtensionResources.zh-Hant.xlf create mode 100644 src/Platform/Microsoft.Testing.Extensions.HotReload/TestingPlatformBuilderHook.cs create mode 100644 src/Platform/Microsoft.Testing.Extensions.HotReload/build/Microsoft.Testing.Extensions.HotReload.props create mode 100644 src/Platform/Microsoft.Testing.Extensions.HotReload/buildMultiTargeting/Microsoft.Testing.Extensions.HotReload.props create mode 100644 src/Platform/Microsoft.Testing.Extensions.HotReload/buildTransitive/Microsoft.Testing.Extensions.HotReload.props create mode 100644 src/Platform/Microsoft.Testing.Extensions.Retry/.editorconfig create mode 100644 src/Platform/Microsoft.Testing.Extensions.Retry/BannedSymbols.txt create mode 100644 src/Platform/Microsoft.Testing.Extensions.Retry/Microsoft.Testing.Extensions.Retry.csproj create mode 100644 src/Platform/Microsoft.Testing.Extensions.Retry/PACKAGE.md create mode 100644 src/Platform/Microsoft.Testing.Extensions.Retry/PublicAPI/PublicAPI.Shipped.txt create mode 100644 src/Platform/Microsoft.Testing.Extensions.Retry/PublicAPI/PublicAPI.Unshipped.txt create mode 100644 src/Platform/Microsoft.Testing.Extensions.Retry/RandomId.cs create mode 100644 src/Platform/Microsoft.Testing.Extensions.Retry/Resources/ExtensionResources.Designer.cs create mode 100644 src/Platform/Microsoft.Testing.Extensions.Retry/Resources/ExtensionResources.resx create mode 100644 src/Platform/Microsoft.Testing.Extensions.Retry/Resources/xlf/ExtensionResources.cs.xlf create mode 100644 src/Platform/Microsoft.Testing.Extensions.Retry/Resources/xlf/ExtensionResources.de.xlf create mode 100644 src/Platform/Microsoft.Testing.Extensions.Retry/Resources/xlf/ExtensionResources.es.xlf create mode 100644 src/Platform/Microsoft.Testing.Extensions.Retry/Resources/xlf/ExtensionResources.fr.xlf create mode 100644 src/Platform/Microsoft.Testing.Extensions.Retry/Resources/xlf/ExtensionResources.it.xlf create mode 100644 src/Platform/Microsoft.Testing.Extensions.Retry/Resources/xlf/ExtensionResources.ja.xlf create mode 100644 src/Platform/Microsoft.Testing.Extensions.Retry/Resources/xlf/ExtensionResources.ko.xlf create mode 100644 src/Platform/Microsoft.Testing.Extensions.Retry/Resources/xlf/ExtensionResources.pl.xlf create mode 100644 src/Platform/Microsoft.Testing.Extensions.Retry/Resources/xlf/ExtensionResources.pt-BR.xlf create mode 100644 src/Platform/Microsoft.Testing.Extensions.Retry/Resources/xlf/ExtensionResources.ru.xlf create mode 100644 src/Platform/Microsoft.Testing.Extensions.Retry/Resources/xlf/ExtensionResources.tr.xlf create mode 100644 src/Platform/Microsoft.Testing.Extensions.Retry/Resources/xlf/ExtensionResources.zh-Hans.xlf create mode 100644 src/Platform/Microsoft.Testing.Extensions.Retry/Resources/xlf/ExtensionResources.zh-Hant.xlf create mode 100644 src/Platform/Microsoft.Testing.Extensions.Retry/RetryCommandLineOptionsProvider.cs create mode 100644 src/Platform/Microsoft.Testing.Extensions.Retry/RetryDataConsumer.cs create mode 100644 src/Platform/Microsoft.Testing.Extensions.Retry/RetryExecutionFilterFactory.cs create mode 100644 src/Platform/Microsoft.Testing.Extensions.Retry/RetryExtensions.cs create mode 100644 src/Platform/Microsoft.Testing.Extensions.Retry/RetryFailedTestsPipeServer.cs create mode 100644 src/Platform/Microsoft.Testing.Extensions.Retry/RetryLifecycleCallbacks.cs create mode 100644 src/Platform/Microsoft.Testing.Extensions.Retry/RetryOrchestrator.cs create mode 100644 src/Platform/Microsoft.Testing.Extensions.Retry/Serializers/FailedTestRequest.cs create mode 100644 src/Platform/Microsoft.Testing.Extensions.Retry/Serializers/GetListOfFailedTests.cs create mode 100644 src/Platform/Microsoft.Testing.Extensions.Retry/Serializers/GetListOfFailedTestsResponse.cs create mode 100644 src/Platform/Microsoft.Testing.Extensions.Retry/Serializers/TotalTestsRunRequest.cs create mode 100644 src/Platform/Microsoft.Testing.Extensions.Retry/TestingPlatformBuilderHook.cs create mode 100644 src/Platform/Microsoft.Testing.Extensions.Retry/build/Microsoft.Testing.Extensions.Retry.props create mode 100644 src/Platform/Microsoft.Testing.Extensions.Retry/buildMultiTargeting/Microsoft.Testing.Extensions.Retry.props create mode 100644 src/Platform/Microsoft.Testing.Extensions.Retry/buildTransitive/Microsoft.Testing.Extensions.Retry.props create mode 100644 test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/RetryFailedTestsTests.cs create mode 100644 test/UnitTests/Microsoft.Testing.Extensions.UnitTests/RetryTests.cs diff --git a/.editorconfig b/.editorconfig index 59a6b86f57..742a013007 100644 --- a/.editorconfig +++ b/.editorconfig @@ -249,6 +249,7 @@ dotnet_diagnostic.SA1402.severity = none # SA1402: File may only dotnet_diagnostic.SA1515.severity = none # SA1515: Single-line comment should be preceded by blank line dotnet_diagnostic.SA1611.severity = none # SA1611: Element parameters should be documented dotnet_diagnostic.SA1615.severity = none # SA1615: Element return value should be documented +dotnet_diagnostic.SA1633.severity = none # SA1633: File should have header dotnet_diagnostic.SA1649.severity = none # SA1649: File name should match first type name # ----------------------------------------------------------------------------------------- diff --git a/LICENSE.PLATFORMTOOLS.txt b/LICENSE.PLATFORMTOOLS.txt new file mode 100644 index 0000000000..0be60953c8 --- /dev/null +++ b/LICENSE.PLATFORMTOOLS.txt @@ -0,0 +1,110 @@ +MICROSOFT SOFTWARE LICENSE TERMS +MICROSOFT TESTING PLATFORM TOOLS + +These license terms are an agreement between you and Microsoft Corporation (or based on where you live, one of its affiliates). They apply to the software named above, and the software listed or available at https://aka.ms/testingplatform/extensions (collectively "Software"). The terms also apply to any Microsoft services and updates for the Software, except to the extent those have different terms. + +IF YOU COMPLY WITH THESE LICENSE TERMS, YOU HAVE THE RIGHTS BELOW. + +1. INSTALLATION AND USE RIGHTS. + +(a) Individuals. If you are an individual working on your own applications, either to sell or for any other purpose, you may install and use the Software to develop and test your applications. + +(b) Organizations. If you are an organization, your users may install and use the Software as follows: + +(i) Any number of your users may use the Software to develop and test applications released under Open Source Initiative ("OSI") approved open source Software licenses. + +(ii) Any number of your users may use the Software to develop and test your applications as part of online or in person classroom training and education, or for performing academic research. + +(iii) If none of the above apply, and you are also not an Enterprise (defined below), then up to five (5) of your individual users can use the Software concurrently to develop and test your applications. + +(iv) If you are an Enterprise, your users may not use the Software to develop or test your applications, except for: (1) open source; and (2) education purposes, each as permitted above. + +An "Enterprise" is any organization and its Affiliates that collectively have either: (A) more than two-hundred fifty (250) PCs or users; or (B) one million ($1,000,000.00) U.S. dollars (or the equivalent in other currencies) in annual revenues. As used in this section, "Affiliates" means those entities that control (via majority ownership), are controlled by, or are under common control with an organization. + +(v) Notwithstanding Sections 1(b)(i)-(v), your users may install and use copies of the Software on your devices to develop and test applications while you have a valid paid entitlement (e.g., a paid subscription) to use at least one (1) eligible product or service listed in the supplemental licensing documentation available at https://aka.ms/vs/eligible-entitlements. Additionally: + +- the number of your users permitted to use the Software, and other relevant limitations, are specified in the supplemental licensing documentation referenced above; and + +- paid entitlements to the eligible products and services require a separate agreement with Microsoft or a Microsoft affiliate. + +(vi) You are responsible for your users' compliance with the terms of this agreement, as applicable. + +(c) Permitted Environments. You may not separate the components of the Software (except as otherwise stated in this agreement) and run those in a production environment, or on third party devices, or for any purpose other than developing and testing your applications. + +(d) Backup Copy. You may make one (1) backup copy of the Software, for reinstalling the Software. + +(e) Online Services in the Software. Some features of the Software make use of online services to provide you information about updates to the Software or extensions, or to enable you to retrieve content, collaborate with others, or otherwise supplement your development experience. As used throughout this agreement, the term "Software" includes these online service features, and by using them, you consent to the transmission of information as described in Section 3 (Data). + +(f) Demo Use. The use rights permitted above include using the Software to demonstrate your applications. + +(g) Third Party Components. The Software may include third party components with separate legal notices or governed by other agreements, as may be described in the notices file(s) accompanying the Software. + +(h) Use on Azure. Running the Software on Microsoft Azure may require separate online usage fees. + +(i) Previews. Microsoft may also make preview versions of the Software available to you. Preview versions of the Software follow semantic versioning, as that standard is described here, https://semver.org/, and preview versions may be released on multiple websites (see https://aka.ms/testingplatform/extensions). You may use any number of copies of the preview Software, components, or features consistent with the terms of these license terms. Previews are experimental and may be substantially different from the commercially released version. They may not operate correctly or work the way a final version will. Microsoft may change previews for the final, commercial version, if any. Microsoft is not obligated to provide maintenance, technical support or updates to you for previews. + +2. FEEDBACK. If you give feedback about the Software to Microsoft, you give to Microsoft, without charge, the right to use, share, and commercialize your feedback in any way and for any purpose. You will not give feedback that is subject to a license that requires Microsoft to license its Software or documentation to third parties because we include your feedback in them. These rights survive this agreement. + +3. DATA. + +(a) Data Collection. The Software may collect information about you and your use of the Software, and send that to Microsoft. Microsoft may use this information to provide services and improve our products and services. You may opt-out of many of these scenarios, but not all, as described in the Software documentation. There are also some features in the Software that may enable you and Microsoft to collect data from users of your applications. If you use these features, you must comply with applicable law, including providing appropriate notices to users of your applications together with a copy of Microsoft's privacy statement. Our privacy statement is located at https://aka.ms/privacy. You can learn more about data collection and its use in the Software documentation and our privacy statement. Your use of the Software operates as your consent to these practices. + +(b) Processing of Personal Data. To the extent Microsoft is a processor or subprocessor of personal data in connection with the software, Microsoft makes the commitments in the European Union General Data Protection Regulation Terms of the Microsoft Products and Services Data Protection Addendum to all customers effective May 25, 2018, at https://learn.microsoft.com/legal/gdpr. + +4. SCOPE OF LICENSE. The Software is licensed, not sold. These license terms only gives you some rights to use the Software. Microsoft reserves all other rights. Unless applicable law gives you more rights despite this limitation, you may use the Software only as expressly permitted in these license terms. In doing so, you must comply with any technical limitations in the Software that only allow you to use it in certain ways. For example, if Microsoft technically limits or disables extensibility for the Software, you may not extend the Software by, among other things, loading or injecting into the Software any non-Microsoft add-ins, macros, or packages; modifying the software registry settings; or adding features or functionality equivalent to that found in Microsoft products and services. In addition, you may not: + +(a) work around any technical limitations in the Software; + +(b) reverse engineer, decompile or disassemble the Software, or otherwise attempt to derive the source code for the Software, except and only to the extent required by third party licensing terms governing use of certain open-source components that may be included with the Software; + +(c) remove, minimize, block or modify any notices of Microsoft or its suppliers in the Software; + +(d) use the Software in any way that is against the law; + +(e) share, publish, rent, or lease the Software; or + +(f) provide the Software as a stand-alone offering or combine it with any of your applications for others to use, or transfer the Software or this agreement to any third party. + +5. EXPORT RESTRICTIONS. You must comply with all domestic and international export laws and regulations that apply to the Software, which include restrictions on destinations, end users and end use. For further information on export restrictions, visit (aka.ms/exporting). + +6. SUPPORT SERVICES. Because this Software is "as is," we may not provide support services for it. + +7. ENTIRE AGREEMENT. This agreement, and the terms for supplements, updates, Internet-based services and support services that you use, are the entire agreement for the Software and support services. + +8. APPLICABLE LAW. If you acquired the Software in the United States, Washington law applies to interpretation of and claims for breach of this agreement, and the laws of the state where you live apply to all other claims. If you acquired the Software in any other country, its laws apply. + +9. CONSUMER RIGHTS; REGIONAL VARIATIONS. This agreement describes certain legal rights. You may have other rights, including consumer rights, under the laws of your state or country. Separate and apart from your relationship with Microsoft, you may also have rights with respect to the party from which you acquired the Software. This agreement does not change those other rights if the laws of your state or country do not permit it to do so. For example, if you acquired the Software in one of the below regions, or mandatory country law applies, then the following provisions apply to you: + +(a) Australia. You have statutory guarantees under the Australian Consumer Law and nothing in this agreement is intended to affect those rights. + +(b) Canada. If you acquired this Software in Canada, you may stop receiving updates by turning off the automatic update feature, disconnecting your device from the Internet (if and when you re-connect to the Internet, however, the Software will resume checking for and installing updates), or uninstalling the Software. The product documentation, if any, may also specify how to turn off updates for your specific device or Software. + +(c) Germany and Austria. + +(i) Warranty. The properly licensed Software will perform substantially as described in any Microsoft materials that accompany the Software. However, Microsoft gives no contractual guarantee in relation to the licensed Software. + +(ii) Limitation of Liability. In case of intentional conduct, gross negligence, claims based on the Product Liability Act, as well as, in case of death or personal or physical injury, Microsoft is liable according to the statutory law. + +Subject to the foregoing clause (ii), Microsoft will only be liable for slight negligence if Microsoft is in breach of such material contractual obligations, the fulfillment of which facilitate the due performance of this agreement, the breach of which would endanger the purpose of this agreement and the compliance with which a party may constantly trust in (so-called "cardinal obligations"). In other cases of slight negligence, Microsoft will not be liable for slight negligence. + +10. DISCLAIMER OF WARRANTY. THE SOFTWARE IS LICENSED "AS-IS." YOU BEAR THE RISK OF USING IT. MICROSOFT GIVES NO EXPRESS WARRANTIES, GUARANTEES OR CONDITIONS. TO THE EXTENT PERMITTED UNDER YOUR LOCAL LAWS, MICROSOFT EXCLUDES THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. + +11. LIMITATION ON AND EXCLUSION OF DAMAGES. YOU CAN RECOVER FROM MICROSOFT AND ITS SUPPLIERS ONLY DIRECT DAMAGES UP TO U.S. $5.00. YOU CANNOT RECOVER ANY OTHER DAMAGES, INCLUDING CONSEQUENTIAL, LOST PROFITS, SPECIAL, INDIRECT OR INCIDENTAL DAMAGES. + +This limitation applies to (a) anything related to the Software, services, content (including code) on third party Internet sites, or third party applications; and (b) claims for breach of contract, breach of warranty, guarantee or condition, strict liability, negligence, or other tort to the extent permitted by applicable law. It also applies even if Microsoft knew or should have known about the possibility of the damages. The above limitation or exclusion may not apply to you because your country may not allow the exclusion or limitation of incidental, consequential or other damages. + +Please note: As this Software is distributed in Quebec, Canada, some of the clauses in this agreement are provided below in French. + +Remarque: Ce Logiciel étant distribué au Québec, Canada, certaines des clauses dans ce contrat sont fournies ci-dessous en français. + +EXONÉRATION DE GARANTIE. Le Logiciel visé par une licence est offert « tel quel ». Toute utilisation de ce Logiciel est à votre seule risque et péril. Microsoft n'accorde aucune autre garantie expresse. Vous pouvez bénéficier de droits additionnels en vertu du droit local sur la protection des consommateurs, que ce contrat ne peut modifier. Là àu elles sont permises par le droit locale, les garanties implicites de qualité marchande, d'adéquation à un usage particulier et d'absence de contrefaçon sont exclues. + +LIMITATION DES DOMMAGES-INTÉRÊTS ET EXCLUSION DE RESPONSABILITÉ POUR LES DOMMAGES. Vous pouvez obtenir de Microsoft et de ses fournisseurs une indemnisation en cas de dommages directs uniquement à hauteur de 5,00 $ US. Vous ne pouvez prétendre à aucune indemnisation pour les autres dommages, y compris les dommages spéciaux, indirects ou accessoires et pertes de bénéfices. Cette limitation concerne: + +- tout ce qui est relié au Logiciel, aux services ou au contenu (y compris le code) figurant sur des sites Internet tiers ou dans des programmes tiers; et + +- les réclamations au titre de violation de contrat ou de garantie, ou au titre de responsabilité stricte, de négligence ou d'une autre faute dans la limite autorisée par la loi en vigueur. +Elle s'applique également, même si Microsoft connaissait ou devrait connaître l'éventualité d'un tel dommage. Si votre pays n'autorise pas l'exclusion ou la limitation de responsabilité pour les dommages indirects, accessoires ou de quelque nature que ce soit, il se peut que la limitation ou l'exclusion ci-dessus ne s'appliquera pas à votre égard. + +EFFET JURIDIQUE. Le présent contrat décrit certains droits juridiques. Vous pourriez avoir d'autres droits prévus par les lois de votre pays. Le présent contrat ne modifie pas les droits que vous confèrent les lois de votre pays si celles-ci ne le permettent pas. + diff --git a/TestFx.sln b/TestFx.sln index 2a974cdced..d014ea3643 100644 --- a/TestFx.sln +++ b/TestFx.sln @@ -56,7 +56,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "eng", "eng", "{FE0DF239-0D8 eng\Build.props = eng\Build.props eng\coverage.config = eng\coverage.config eng\install-windows-sdk.ps1 = eng\install-windows-sdk.ps1 - eng\stylecop.json = eng\stylecop.json eng\verify-nupkgs.ps1 = eng\verify-nupkgs.ps1 eng\Version.Details.xml = eng\Version.Details.xml eng\Versions.props = eng\Versions.props @@ -211,6 +210,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Testing.Extension 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 Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -489,6 +492,14 @@ Global {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 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -574,6 +585,8 @@ Global {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} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {31E0F4D5-975A-41CC-933E-545B2201FAF9} diff --git a/eng/Analyzers.props b/eng/Analyzers.props index a9699b919e..1283f82e91 100644 --- a/eng/Analyzers.props +++ b/eng/Analyzers.props @@ -12,7 +12,6 @@ - diff --git a/eng/stylecop.json b/eng/stylecop.json deleted file mode 100644 index 219f294da1..0000000000 --- a/eng/stylecop.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "$schema": "https://raw.githubusercontent.com/DotNetAnalyzers/StyleCopAnalyzers/master/StyleCop.Analyzers/StyleCop.Analyzers/Settings/stylecop.schema.json", - "settings": { - "documentationRules": { - "companyName": "Microsoft Corporation", - "copyrightText": "Copyright (c) {companyName}. All rights reserved.\nLicensed under the MIT license. See LICENSE file in the project root for full license information.", - "xmlHeader": false, - "fileNamingConvention": "metadata" - } - } -} diff --git a/eng/stylecop.ruleset b/eng/stylecop.ruleset deleted file mode 100644 index bba4a0b95c..0000000000 --- a/eng/stylecop.ruleset +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - - - - - - - diff --git a/eng/stylecop.test.ruleset b/eng/stylecop.test.ruleset deleted file mode 100644 index 40fc485f7c..0000000000 --- a/eng/stylecop.test.ruleset +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - - - - - - - - - - - - - - - diff --git a/src/Platform/Microsoft.Testing.Extensions.HotReload/.editorconfig b/src/Platform/Microsoft.Testing.Extensions.HotReload/.editorconfig new file mode 100644 index 0000000000..7d7bac49f4 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.HotReload/.editorconfig @@ -0,0 +1,2 @@ +[*.{cs,vb}] +file_header_template = Copyright (c) Microsoft Corporation. All rights reserved.\nLicensed under dual-license. See LICENSE.PLATFORMTOOLS.txt file in the project root for full license information. diff --git a/src/Platform/Microsoft.Testing.Extensions.HotReload/BannedSymbols.txt b/src/Platform/Microsoft.Testing.Extensions.HotReload/BannedSymbols.txt new file mode 100644 index 0000000000..5bc5c2fb96 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.HotReload/BannedSymbols.txt @@ -0,0 +1,10 @@ +T:System.ArgumentNullException; Use 'Guard' instead +P:System.DateTime.Now; Use 'IClock' instead +P:System.DateTime.UtcNow; Use 'IClock' instead +M:System.Threading.Tasks.Task.Run(System.Action); Use 'ITask' instead +M:System.Threading.Tasks.Task.WhenAll(System.Threading.Tasks.Task[]); Use 'ITask' instead +M:System.Threading.Tasks.Task.WhenAll(System.Collections.Generic.IEnumerable{System.Threading.Tasks.Task}); Use 'ITask' instead +M:System.String.IsNullOrEmpty(System.String); Use 'RoslynString.IsNullOrEmpty' instead +M:System.String.IsNullOrWhiteSpace(System.String); Use 'RoslynString.IsNullOrWhiteSpace' instead +M:System.Diagnostics.Debug.Assert(System.Boolean); Use 'RoslynDebug.Assert' instead +M:System.Diagnostics.Debug.Assert(System.Boolean,System.String); Use 'RoslynDebug.Assert' instead diff --git a/src/Platform/Microsoft.Testing.Extensions.HotReload/HotReloadExtensions.cs b/src/Platform/Microsoft.Testing.Extensions.HotReload/HotReloadExtensions.cs new file mode 100644 index 0000000000..c0578a889b --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.HotReload/HotReloadExtensions.cs @@ -0,0 +1,15 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under dual-license. See LICENSE.PLATFORMTOOLS.txt file in the project root for full license information. + +using Microsoft.Testing.Extensions.Hosting; +using Microsoft.Testing.Platform.Builder; +using Microsoft.Testing.Platform.TestHost; + +namespace Microsoft.Testing.Extensions; + +public static class HotReloadExtensions +{ + public static void AddHotReloadProvider(this ITestApplicationBuilder builder) + => ((TestHostManager)builder.TestHost).AddTestFrameworkInvoker(serviceProvider => + new HotReloadTestHostTestFrameworkInvoker(serviceProvider)); +} diff --git a/src/Platform/Microsoft.Testing.Extensions.HotReload/HotReloadHandler.cs b/src/Platform/Microsoft.Testing.Extensions.HotReload/HotReloadHandler.cs new file mode 100644 index 0000000000..669ee80882 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.HotReload/HotReloadHandler.cs @@ -0,0 +1,91 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under dual-license. See LICENSE.PLATFORMTOOLS.txt file in the project root for full license information. + +using Microsoft.Testing.Extensions.Hosting.Resources; +using Microsoft.Testing.Platform.Extensions.OutputDevice; +using Microsoft.Testing.Platform.Helpers; +using Microsoft.Testing.Platform.OutputDevice; + +#if NETCOREAPP +using System.Reflection.Metadata; + +using Microsoft.Testing.Extensions.Hosting; + +[assembly: MetadataUpdateHandler(typeof(HotReloadHandler))] +#endif + +namespace Microsoft.Testing.Extensions.Hosting; + +internal sealed class HotReloadHandler +{ + private static readonly SemaphoreSlim SemaphoreSlim = new(1, 1); + private static bool s_shutdownProcess; + private readonly IConsole _console; + private readonly IOutputDevice _outputDevice; + private readonly IOutputDeviceDataProducer _outputDeviceDataProducer; + + public HotReloadHandler(IConsole console, IOutputDevice outputDevice, IOutputDeviceDataProducer outputDeviceDataProducer) + { + _console = console; + _outputDevice = outputDevice; + _outputDeviceDataProducer = outputDeviceDataProducer; + _console.CancelKeyPress += (_, _) => + { + if (!s_shutdownProcess) + { + s_shutdownProcess = true; + SemaphoreSlim.Release(); + } + }; + } + + // Called automatically by the runtime through the MetadataUpdateHandlerAttribute + public static void ClearCache(Type[]? _) + { + } + + // Called automatically by the runtime through the MetadataUpdateHandlerAttribute + public static void UpdateApplication(Type[]? _) => SemaphoreSlim.Release(); + +#if !NET6_0_OR_GREATER + public Task ShouldRunAsync(Task? waitExecutionCompletion, CancellationToken cancellationToken) + { + // Avoid warnings about unused parameters and fields. + _ = _outputDevice; + _ = _outputDeviceDataProducer; + _ = waitExecutionCompletion; + _ = cancellationToken; + throw new NotSupportedException(ExtensionResources.HotReloadHandlerUnsupportedFrameworkErrorMessage); + } +#else + public async Task ShouldRunAsync(Task? waitExecutionCompletion, CancellationToken cancellationToken) + { + if (s_shutdownProcess) + { + return false; + } + + cancellationToken.Register(() => s_shutdownProcess = true); + + if (waitExecutionCompletion is not null) + { + await waitExecutionCompletion; + await _outputDevice!.DisplayAsync(_outputDeviceDataProducer, new TextOutputDeviceData(ExtensionResources.HotReloadSessionCompleted)); + } + + try + { + await SemaphoreSlim.WaitAsync(cancellationToken); + } + catch (OperationCanceledException) when (cancellationToken.IsCancellationRequested) + { + // We're closing + } + + _console!.Clear(); + await _outputDevice.DisplayAsync(_outputDeviceDataProducer, new TextOutputDeviceData(ExtensionResources.HotReloadSessionStarted)); + + return !s_shutdownProcess; + } +#endif +} diff --git a/src/Platform/Microsoft.Testing.Extensions.HotReload/HotReloadTestHostTestFrameworkInvoker.cs b/src/Platform/Microsoft.Testing.Extensions.HotReload/HotReloadTestHostTestFrameworkInvoker.cs new file mode 100644 index 0000000000..df6c08a9d4 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.HotReload/HotReloadTestHostTestFrameworkInvoker.cs @@ -0,0 +1,68 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under dual-license. See LICENSE.PLATFORMTOOLS.txt file in the project root for full license information. + +using Microsoft.Testing.Platform.Extensions.TestFramework; +using Microsoft.Testing.Platform.Helpers; +using Microsoft.Testing.Platform.Messages; +using Microsoft.Testing.Platform.OutputDevice; +using Microsoft.Testing.Platform.Requests; +using Microsoft.Testing.Platform.Services; + +namespace Microsoft.Testing.Extensions.Hosting; + +internal sealed class HotReloadTestHostTestFrameworkInvoker : TestHostTestFrameworkInvoker +{ + private readonly bool _isHotReloadEnabled; + + public HotReloadTestHostTestFrameworkInvoker(IServiceProvider serviceProvider) + : base(serviceProvider) + { + _isHotReloadEnabled = IsHotReloadEnabled(serviceProvider.GetEnvironment()); + if (_isHotReloadEnabled) + { + ((SystemRuntimeFeature)serviceProvider.GetRuntimeFeature()).EnableHotReload(); + } + } + + private static bool IsHotReloadEnabled(IEnvironment environment) + => environment.GetEnvironmentVariable(EnvironmentVariableConstants.DOTNET_WATCH) == "1" + || environment.GetEnvironmentVariable(EnvironmentVariableConstants.TESTINGPLATFORM_HOTRELOAD_ENABLED) == "1"; + + public override async Task ExecuteRequestAsync(ITestFramework testFrameworkAdapter, TestExecutionRequest request, + IMessageBus messageBus, CancellationToken cancellationToken) + { + if (!_isHotReloadEnabled) + { + await base.ExecuteRequestAsync(testFrameworkAdapter, request, messageBus, cancellationToken); + return; + } + + // Using the output device here rather than Console WriteLine ensures that we don't break live logger output. + IOutputDevice outputDevice = ServiceProvider.GetOutputDevice(); + var hotReloadHandler = new HotReloadHandler(ServiceProvider.GetConsole(), outputDevice, this); + TaskCompletionSource? executionCompleted = null; + while (await hotReloadHandler.ShouldRunAsync(executionCompleted?.Task, ServiceProvider.GetTestApplicationCancellationTokenSource().CancellationToken)) + { + executionCompleted = new(); + using SemaphoreSlim requestSemaphore = new(1); + var hotReloadOutputDevice = ServiceProvider.GetPlatformOutputDevice() as IHotReloadPlatformOutputDevice; + if (hotReloadOutputDevice is not null) + { + await hotReloadOutputDevice.DisplayBeforeHotReloadSessionStartAsync(); + } + +#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 requestSemaphore.WaitAsync(cancellationToken); + await ServiceProvider.GetBaseMessageBus().DrainDataAsync(); + if (hotReloadOutputDevice is not null) + { + await hotReloadOutputDevice.DisplayAfterHotReloadSessionEndAsync(); + } + + 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 new file mode 100644 index 0000000000..a7e8e9b6f7 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.HotReload/Microsoft.Testing.Extensions.HotReload.csproj @@ -0,0 +1,61 @@ + + + + netstandard2.0;$(MicrosoftTestingTargetFrameworks) + Microsoft.Testing.Extensions.Hosting + + + License.txt + + + + + + + + + + + + + + + + + + + + true + buildMultiTargeting + + + + buildTransitive/$(TargetFramework) + + + build/$(TargetFramework) + + + + + + + + + + True + True + ExtensionResources.resx + + + + + + ResXFileCodeGenerator + ExtensionResources.Designer.cs + + + + \ No newline at end of file diff --git a/src/Platform/Microsoft.Testing.Extensions.HotReload/PACKAGE.md b/src/Platform/Microsoft.Testing.Extensions.HotReload/PACKAGE.md new file mode 100644 index 0000000000..7975720586 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.HotReload/PACKAGE.md @@ -0,0 +1,9 @@ +# Microsoft.Testing + +Microsoft Testing is a set of platform, framework and protocol intended to make it possible to run any test on any target or device. + +Documentation can be found at . + +## About + +This package extends Microsoft Testing Platform to provide Hot Reload support. diff --git a/src/Platform/Microsoft.Testing.Extensions.HotReload/PublicAPI/PublicAPI.Shipped.txt b/src/Platform/Microsoft.Testing.Extensions.HotReload/PublicAPI/PublicAPI.Shipped.txt new file mode 100644 index 0000000000..16e56fe341 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.HotReload/PublicAPI/PublicAPI.Shipped.txt @@ -0,0 +1,5 @@ +#nullable enable +Microsoft.Testing.Extensions.HotReload.TestingPlatformBuilderHook +Microsoft.Testing.Extensions.HotReloadExtensions +static Microsoft.Testing.Extensions.HotReload.TestingPlatformBuilderHook.AddExtensions(Microsoft.Testing.Platform.Builder.ITestApplicationBuilder! testApplicationBuilder, string![]! _) -> void +static Microsoft.Testing.Extensions.HotReloadExtensions.AddHotReloadProvider(this Microsoft.Testing.Platform.Builder.ITestApplicationBuilder! builder) -> void diff --git a/src/Platform/Microsoft.Testing.Extensions.HotReload/PublicAPI/PublicAPI.Unshipped.txt b/src/Platform/Microsoft.Testing.Extensions.HotReload/PublicAPI/PublicAPI.Unshipped.txt new file mode 100644 index 0000000000..7dc5c58110 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.HotReload/PublicAPI/PublicAPI.Unshipped.txt @@ -0,0 +1 @@ +#nullable enable diff --git a/src/Platform/Microsoft.Testing.Extensions.HotReload/Resources/ExtensionResources.Designer.cs b/src/Platform/Microsoft.Testing.Extensions.HotReload/Resources/ExtensionResources.Designer.cs new file mode 100644 index 0000000000..4e6bc289c6 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.HotReload/Resources/ExtensionResources.Designer.cs @@ -0,0 +1,90 @@ +//------------------------------------------------------------------------------ +// +// 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.HotReload/Resources/ExtensionResources.resx b/src/Platform/Microsoft.Testing.Extensions.HotReload/Resources/ExtensionResources.resx new file mode 100644 index 0000000000..931f443665 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.HotReload/Resources/ExtensionResources.resx @@ -0,0 +1,129 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 + + + Hot reload is only supported on .NET 6.0 or greater + + + Hot reload test session completed + + + Hot reload test session started + + \ No newline at end of file diff --git a/src/Platform/Microsoft.Testing.Extensions.HotReload/Resources/xlf/ExtensionResources.cs.xlf b/src/Platform/Microsoft.Testing.Extensions.HotReload/Resources/xlf/ExtensionResources.cs.xlf new file mode 100644 index 0000000000..bf24eb49fe --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.HotReload/Resources/xlf/ExtensionResources.cs.xlf @@ -0,0 +1,22 @@ + + + + + + Hot reload is only supported on .NET 6.0 or greater + OpÄ›tovné naÄítání za provozu se podporuje pouze v rozhraní .NET 6.0 nebo novÄ›jším. + + + + Hot reload test session completed + Testovací relace opÄ›tovného naÄítání za provozu byla dokonÄena. + + + + Hot reload test session started + Testovací relace opÄ›tovného naÄítání za provozu byla spuÅ¡tÄ›na. + + + + + \ No newline at end of file diff --git a/src/Platform/Microsoft.Testing.Extensions.HotReload/Resources/xlf/ExtensionResources.de.xlf b/src/Platform/Microsoft.Testing.Extensions.HotReload/Resources/xlf/ExtensionResources.de.xlf new file mode 100644 index 0000000000..426d75c690 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.HotReload/Resources/xlf/ExtensionResources.de.xlf @@ -0,0 +1,22 @@ + + + + + + Hot reload is only supported on .NET 6.0 or greater + Hot Reload wird nur unter .NET 6.0 oder höher unterstützt. + + + + Hot reload test session completed + Hot Reload-Testsitzung abgeschlossen + + + + Hot reload test session started + Hot Reload-Testsitzung gestartet + + + + + \ No newline at end of file diff --git a/src/Platform/Microsoft.Testing.Extensions.HotReload/Resources/xlf/ExtensionResources.es.xlf b/src/Platform/Microsoft.Testing.Extensions.HotReload/Resources/xlf/ExtensionResources.es.xlf new file mode 100644 index 0000000000..e014452c97 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.HotReload/Resources/xlf/ExtensionResources.es.xlf @@ -0,0 +1,22 @@ + + + + + + Hot reload is only supported on .NET 6.0 or greater + La recarga activa solo se admite en .NET 6.0 o posterior + + + + Hot reload test session completed + Sesión de prueba de recarga activa completada + + + + Hot reload test session started + Se inició la sesión de prueba de recarga activa + + + + + \ No newline at end of file diff --git a/src/Platform/Microsoft.Testing.Extensions.HotReload/Resources/xlf/ExtensionResources.fr.xlf b/src/Platform/Microsoft.Testing.Extensions.HotReload/Resources/xlf/ExtensionResources.fr.xlf new file mode 100644 index 0000000000..311d81deac --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.HotReload/Resources/xlf/ExtensionResources.fr.xlf @@ -0,0 +1,22 @@ + + + + + + Hot reload is only supported on .NET 6.0 or greater + Le rechargement à chaud est uniquement pris en charge sur .NET 6.0 ou version ultérieure + + + + Hot reload test session completed + Session de test de rechargement à chaud terminée + + + + Hot reload test session started + Session de test de rechargement à chaud démarrée + + + + + \ No newline at end of file diff --git a/src/Platform/Microsoft.Testing.Extensions.HotReload/Resources/xlf/ExtensionResources.it.xlf b/src/Platform/Microsoft.Testing.Extensions.HotReload/Resources/xlf/ExtensionResources.it.xlf new file mode 100644 index 0000000000..f8c8801628 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.HotReload/Resources/xlf/ExtensionResources.it.xlf @@ -0,0 +1,22 @@ + + + + + + Hot reload is only supported on .NET 6.0 or greater + Il ricaricamento rapido è supportato solo in .NET 6.0 o versione successiva + + + + Hot reload test session completed + Sessione di test di ricaricamento rapido completata + + + + Hot reload test session started + Sessione di test di ricaricamento rapido avviata + + + + + \ No newline at end of file diff --git a/src/Platform/Microsoft.Testing.Extensions.HotReload/Resources/xlf/ExtensionResources.ja.xlf b/src/Platform/Microsoft.Testing.Extensions.HotReload/Resources/xlf/ExtensionResources.ja.xlf new file mode 100644 index 0000000000..73be365a65 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.HotReload/Resources/xlf/ExtensionResources.ja.xlf @@ -0,0 +1,22 @@ + + + + + + Hot reload is only supported on .NET 6.0 or greater + ホット リロード㯠.NET 6.0 以é™ã§ã®ã¿ã‚µãƒãƒ¼ãƒˆã•ã‚Œã¦ã„ã¾ã™ + + + + Hot reload test session completed + ホット リロード テスト セッションãŒå®Œäº†ã—ã¾ã—㟠+ + + + Hot reload test session started + ホット リロード テスト セッションãŒé–‹å§‹ã•ã‚Œã¾ã—㟠+ + + + + \ No newline at end of file diff --git a/src/Platform/Microsoft.Testing.Extensions.HotReload/Resources/xlf/ExtensionResources.ko.xlf b/src/Platform/Microsoft.Testing.Extensions.HotReload/Resources/xlf/ExtensionResources.ko.xlf new file mode 100644 index 0000000000..6dbdebaba2 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.HotReload/Resources/xlf/ExtensionResources.ko.xlf @@ -0,0 +1,22 @@ + + + + + + Hot reload is only supported on .NET 6.0 or greater + í•« 다시 로드는 .NET 6.0 ì´ìƒì—서만 지ì›ë©ë‹ˆë‹¤. + + + + Hot reload test session completed + í•« 다시 로드 테스트 세션 완료 + + + + Hot reload test session started + í•« 다시 로드 테스트 세션 ì‹œìž‘ë¨ + + + + + \ No newline at end of file diff --git a/src/Platform/Microsoft.Testing.Extensions.HotReload/Resources/xlf/ExtensionResources.pl.xlf b/src/Platform/Microsoft.Testing.Extensions.HotReload/Resources/xlf/ExtensionResources.pl.xlf new file mode 100644 index 0000000000..5b74462a80 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.HotReload/Resources/xlf/ExtensionResources.pl.xlf @@ -0,0 +1,22 @@ + + + + + + Hot reload is only supported on .NET 6.0 or greater + Ponowne Å‚adowanie na gorÄ…co jest obsÅ‚ugiwane tylko na platformie .NET 6.0 lub nowszej + + + + Hot reload test session completed + Sesja testu ponownego Å‚adowania na gorÄ…co zostaÅ‚a ukoÅ„czona + + + + Hot reload test session started + RozpoczÄ™to sesjÄ™ testu ponownego Å‚adowania na gorÄ…co + + + + + \ No newline at end of file diff --git a/src/Platform/Microsoft.Testing.Extensions.HotReload/Resources/xlf/ExtensionResources.pt-BR.xlf b/src/Platform/Microsoft.Testing.Extensions.HotReload/Resources/xlf/ExtensionResources.pt-BR.xlf new file mode 100644 index 0000000000..743f052c79 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.HotReload/Resources/xlf/ExtensionResources.pt-BR.xlf @@ -0,0 +1,22 @@ + + + + + + Hot reload is only supported on .NET 6.0 or greater + Só há suporte para a recarga dinâmica no .NET 6.0 ou superior + + + + Hot reload test session completed + Sessão de teste de recarga dinâmica concluída + + + + Hot reload test session started + Sessão de teste de recarga dinâmica iniciada + + + + + \ No newline at end of file diff --git a/src/Platform/Microsoft.Testing.Extensions.HotReload/Resources/xlf/ExtensionResources.ru.xlf b/src/Platform/Microsoft.Testing.Extensions.HotReload/Resources/xlf/ExtensionResources.ru.xlf new file mode 100644 index 0000000000..1f26adfb86 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.HotReload/Resources/xlf/ExtensionResources.ru.xlf @@ -0,0 +1,22 @@ + + + + + + Hot reload is only supported on .NET 6.0 or greater + ГорÑÑ‡Ð°Ñ Ð¿ÐµÑ€ÐµÐ·Ð°Ð³Ñ€ÑƒÐ·ÐºÐ° поддерживаетÑÑ Ñ‚Ð¾Ð»ÑŒÐºÐ¾ в верÑии .NET 6.0 или более поздних верÑиÑÑ… + + + + Hot reload test session completed + ТеÑтовый ÑÐµÐ°Ð½Ñ Ð³Ð¾Ñ€Ñчей перезагрузки завершен + + + + Hot reload test session started + Запущен теÑтовый ÑÐµÐ°Ð½Ñ Ð³Ð¾Ñ€Ñчей перезагрузки + + + + + \ No newline at end of file diff --git a/src/Platform/Microsoft.Testing.Extensions.HotReload/Resources/xlf/ExtensionResources.tr.xlf b/src/Platform/Microsoft.Testing.Extensions.HotReload/Resources/xlf/ExtensionResources.tr.xlf new file mode 100644 index 0000000000..70125a0e05 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.HotReload/Resources/xlf/ExtensionResources.tr.xlf @@ -0,0 +1,22 @@ + + + + + + Hot reload is only supported on .NET 6.0 or greater + Çalışırken yeniden yükleme yalnızca .NET 6.0 veya üzeri sürümlerde desteklenir + + + + Hot reload test session completed + Çalışırken yeniden yükleme test oturumu tamamlandı + + + + Hot reload test session started + Çalışırken yeniden yükleme test oturumu baÅŸlatıldı + + + + + \ No newline at end of file diff --git a/src/Platform/Microsoft.Testing.Extensions.HotReload/Resources/xlf/ExtensionResources.zh-Hans.xlf b/src/Platform/Microsoft.Testing.Extensions.HotReload/Resources/xlf/ExtensionResources.zh-Hans.xlf new file mode 100644 index 0000000000..af518ebd73 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.HotReload/Resources/xlf/ExtensionResources.zh-Hans.xlf @@ -0,0 +1,22 @@ + + + + + + Hot reload is only supported on .NET 6.0 or greater + ä»… .NET 6.0 或更高版本支æŒçƒ­é‡è½½ + + + + Hot reload test session completed + 热é‡è½½æµ‹è¯•ä¼šè¯å·²å®Œæˆ + + + + Hot reload test session started + 热é‡è½½æµ‹è¯•ä¼šè¯å·²å¯åŠ¨ + + + + + \ No newline at end of file diff --git a/src/Platform/Microsoft.Testing.Extensions.HotReload/Resources/xlf/ExtensionResources.zh-Hant.xlf b/src/Platform/Microsoft.Testing.Extensions.HotReload/Resources/xlf/ExtensionResources.zh-Hant.xlf new file mode 100644 index 0000000000..0fd9224ca7 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.HotReload/Resources/xlf/ExtensionResources.zh-Hant.xlf @@ -0,0 +1,22 @@ + + + + + + Hot reload is only supported on .NET 6.0 or greater + åªæœ‰ .NET 6.0 或更新版本æ‰æ”¯æ´ç†±é‡æ–°è¼‰å…¥ + + + + Hot reload test session completed + 熱é‡æ–°è¼‰å…¥æ¸¬è©¦å·¥ä½œéšŽæ®µå·²å®Œæˆ + + + + Hot reload test session started + 熱é‡æ–°è¼‰å…¥æ¸¬è©¦å·¥ä½œéšŽæ®µå·²é–‹å§‹ + + + + + \ No newline at end of file diff --git a/src/Platform/Microsoft.Testing.Extensions.HotReload/TestingPlatformBuilderHook.cs b/src/Platform/Microsoft.Testing.Extensions.HotReload/TestingPlatformBuilderHook.cs new file mode 100644 index 0000000000..b76e5d5b53 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.HotReload/TestingPlatformBuilderHook.cs @@ -0,0 +1,12 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under dual-license. See LICENSE.PLATFORMTOOLS.txt file in the project root for full license information. + +using Microsoft.Testing.Platform.Builder; + +namespace Microsoft.Testing.Extensions.HotReload; + +public static class TestingPlatformBuilderHook +{ + public static void AddExtensions(ITestApplicationBuilder testApplicationBuilder, string[] _) + => testApplicationBuilder.AddHotReloadProvider(); +} diff --git a/src/Platform/Microsoft.Testing.Extensions.HotReload/build/Microsoft.Testing.Extensions.HotReload.props b/src/Platform/Microsoft.Testing.Extensions.HotReload/build/Microsoft.Testing.Extensions.HotReload.props new file mode 100644 index 0000000000..aaa0f36baa --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.HotReload/build/Microsoft.Testing.Extensions.HotReload.props @@ -0,0 +1,3 @@ + + + diff --git a/src/Platform/Microsoft.Testing.Extensions.HotReload/buildMultiTargeting/Microsoft.Testing.Extensions.HotReload.props b/src/Platform/Microsoft.Testing.Extensions.HotReload/buildMultiTargeting/Microsoft.Testing.Extensions.HotReload.props new file mode 100644 index 0000000000..7fccd7490e --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.HotReload/buildMultiTargeting/Microsoft.Testing.Extensions.HotReload.props @@ -0,0 +1,9 @@ + + + + + Microsoft.Testing.Extensions.HotReload + Microsoft.Testing.Extensions.HotReload.TestingPlatformBuilderHook + + + diff --git a/src/Platform/Microsoft.Testing.Extensions.HotReload/buildTransitive/Microsoft.Testing.Extensions.HotReload.props b/src/Platform/Microsoft.Testing.Extensions.HotReload/buildTransitive/Microsoft.Testing.Extensions.HotReload.props new file mode 100644 index 0000000000..aaa0f36baa --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.HotReload/buildTransitive/Microsoft.Testing.Extensions.HotReload.props @@ -0,0 +1,3 @@ + + + diff --git a/src/Platform/Microsoft.Testing.Extensions.Retry/.editorconfig b/src/Platform/Microsoft.Testing.Extensions.Retry/.editorconfig new file mode 100644 index 0000000000..7d7bac49f4 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.Retry/.editorconfig @@ -0,0 +1,2 @@ +[*.{cs,vb}] +file_header_template = Copyright (c) Microsoft Corporation. All rights reserved.\nLicensed under dual-license. See LICENSE.PLATFORMTOOLS.txt file in the project root for full license information. diff --git a/src/Platform/Microsoft.Testing.Extensions.Retry/BannedSymbols.txt b/src/Platform/Microsoft.Testing.Extensions.Retry/BannedSymbols.txt new file mode 100644 index 0000000000..5bc5c2fb96 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.Retry/BannedSymbols.txt @@ -0,0 +1,10 @@ +T:System.ArgumentNullException; Use 'Guard' instead +P:System.DateTime.Now; Use 'IClock' instead +P:System.DateTime.UtcNow; Use 'IClock' instead +M:System.Threading.Tasks.Task.Run(System.Action); Use 'ITask' instead +M:System.Threading.Tasks.Task.WhenAll(System.Threading.Tasks.Task[]); Use 'ITask' instead +M:System.Threading.Tasks.Task.WhenAll(System.Collections.Generic.IEnumerable{System.Threading.Tasks.Task}); Use 'ITask' instead +M:System.String.IsNullOrEmpty(System.String); Use 'RoslynString.IsNullOrEmpty' instead +M:System.String.IsNullOrWhiteSpace(System.String); Use 'RoslynString.IsNullOrWhiteSpace' instead +M:System.Diagnostics.Debug.Assert(System.Boolean); Use 'RoslynDebug.Assert' instead +M:System.Diagnostics.Debug.Assert(System.Boolean,System.String); Use 'RoslynDebug.Assert' instead diff --git a/src/Platform/Microsoft.Testing.Extensions.Retry/Microsoft.Testing.Extensions.Retry.csproj b/src/Platform/Microsoft.Testing.Extensions.Retry/Microsoft.Testing.Extensions.Retry.csproj new file mode 100644 index 0000000000..d2a73307d6 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.Retry/Microsoft.Testing.Extensions.Retry.csproj @@ -0,0 +1,65 @@ + + + + netstandard2.0;$(MicrosoftTestingTargetFrameworks) + Microsoft.Testing.Extensions.Policy + + + License.txt + + + + + + + + + + + + + + + + + + + + + true + buildMultiTargeting + + + buildTransitive/$(TargetFramework) + + + build/$(TargetFramework) + + + + + + + + + + True + True + ExtensionResources.resx + + + + + + ResXFileCodeGenerator + ExtensionResources.Designer.cs + + + + + + + + diff --git a/src/Platform/Microsoft.Testing.Extensions.Retry/PACKAGE.md b/src/Platform/Microsoft.Testing.Extensions.Retry/PACKAGE.md new file mode 100644 index 0000000000..1d728ad78d --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.Retry/PACKAGE.md @@ -0,0 +1,9 @@ +# Microsoft.Testing + +Microsoft Testing is a set of platform, framework and protocol intended to make it possible to run any test on any target or device. + +Documentation can be found at . + +## About + +This package extends Microsoft Testing Platform to provide a retry policy system that allows to restart tests on failure. diff --git a/src/Platform/Microsoft.Testing.Extensions.Retry/PublicAPI/PublicAPI.Shipped.txt b/src/Platform/Microsoft.Testing.Extensions.Retry/PublicAPI/PublicAPI.Shipped.txt new file mode 100644 index 0000000000..2162ba1c98 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.Retry/PublicAPI/PublicAPI.Shipped.txt @@ -0,0 +1,5 @@ +#nullable enable +Microsoft.Testing.Extensions.Retry.TestingPlatformBuilderHook +Microsoft.Testing.Extensions.RetryExtensions +static Microsoft.Testing.Extensions.Retry.TestingPlatformBuilderHook.AddExtensions(Microsoft.Testing.Platform.Builder.ITestApplicationBuilder! testApplicationBuilder, string![]! _) -> void +static Microsoft.Testing.Extensions.RetryExtensions.AddRetryProvider(this Microsoft.Testing.Platform.Builder.ITestApplicationBuilder! builder) -> void diff --git a/src/Platform/Microsoft.Testing.Extensions.Retry/PublicAPI/PublicAPI.Unshipped.txt b/src/Platform/Microsoft.Testing.Extensions.Retry/PublicAPI/PublicAPI.Unshipped.txt new file mode 100644 index 0000000000..7dc5c58110 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.Retry/PublicAPI/PublicAPI.Unshipped.txt @@ -0,0 +1 @@ +#nullable enable diff --git a/src/Platform/Microsoft.Testing.Extensions.Retry/RandomId.cs b/src/Platform/Microsoft.Testing.Extensions.Retry/RandomId.cs new file mode 100644 index 0000000000..1f1181702a --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.Retry/RandomId.cs @@ -0,0 +1,44 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under dual-license. See LICENSE.PLATFORMTOOLS.txt file in the project root for full license information. + +// Copy from https://github.com/microsoft/testfx/blob/b769496b8992bf8f51e000f7a5626b5ec6bb3d27/test/Utilities/Microsoft.Testing.TestInfrastructure/RandomId.cs +using System.Security.Cryptography; + +namespace Microsoft.Testing.Extensions; + +/// +/// Slightly random id that is just good enough for creating distinct directories for each test. +/// +internal static class RandomId +{ + private const string Pool = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; + private static readonly RandomNumberGenerator Rng = RandomNumberGenerator.Create(); + + /// + /// 5 character long id from 0-9A-Za-z0, for example fUfko, A6uvM, sOMXa, RY1ei, KvdJZ. + /// + public static string Next() => Next(5); + + private static string Next(int length) + { + int poolLength = Pool.Length; + char[] id = new char[length]; + lock (Pool) + { + for (int idIndex = 0; idIndex < length; idIndex++) + { + int poolIndex = poolLength + 1; + while (poolIndex >= poolLength) + { + byte[] bytes = new byte[1]; + Rng.GetNonZeroBytes(bytes); + poolIndex = bytes[0]; + } + + id[idIndex] = Pool[poolIndex]; + } + } + + return new string(id); + } +} diff --git a/src/Platform/Microsoft.Testing.Extensions.Retry/Resources/ExtensionResources.Designer.cs b/src/Platform/Microsoft.Testing.Extensions.Retry/Resources/ExtensionResources.Designer.cs new file mode 100644 index 0000000000..79b07ef2dc --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.Retry/Resources/ExtensionResources.Designer.cs @@ -0,0 +1,284 @@ +//------------------------------------------------------------------------------ +// +// 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/Resources/ExtensionResources.resx b/src/Platform/Microsoft.Testing.Extensions.Retry/Resources/ExtensionResources.resx new file mode 100644 index 0000000000..65122496a7 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.Retry/Resources/ExtensionResources.resx @@ -0,0 +1,204 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 + + + Failed to create retries directory due to collisions in '{0}' despite re-trying. + '{0}' is the directory where collisions happen + + + Failure threshold policy is enabled, failed tests will not be restarted. + + + Maximum failed tests threshold is {0} and {1} tests failed + + + Percentage failed threshold is {0}% and {1}% tests failed ({2}/{3}) + + + +===================== +Moving last attempt asset files to the default result directory +===================== + + + + Moving file '{0}' to '{1}' + + + Failed to start process '{0}' + + + Retry failed tests feature allows to restart test execution upon failure. + + + Retry failed tests + + + Retry failed tests only works with builders of type 'Microsoft.Testing.Platform.Builder.TestApplicationBuilder' + + + Disable retry mechanism if the percentage of failed tests is greater than the specified value + + + Disable retry mechanism if the number of failed tests is greater than the specified value + + + Retry failed tests feature is not supported in hot reload mode + + + Retry failed tests feature is not supported in server mode + + + Enable retry failed tests + + + Option '{0}' requires option '{1}' to be specified + + + Option '{0}' expects a single integer argument + + + Options '{0}' and '{1}' cannot be used together + + + Test host process exited before the retry service could connect to it. Exit code: {0} + + + +===================== +Tests suite completed successfully in {0} attempts +===================== + + + +===================== +Tests suite failed, total failed tests: {0}, exit code: {1}, attempt: {2}/{3} +===================== + + + + +===================== +Tests suite failed in all {0} attempts +===================== + + + Test suite failed with and exit code different that 2 (failed tests). Failure related to an unexpected condition. Exit code '{0}' + + \ No newline at end of file diff --git a/src/Platform/Microsoft.Testing.Extensions.Retry/Resources/xlf/ExtensionResources.cs.xlf b/src/Platform/Microsoft.Testing.Extensions.Retry/Resources/xlf/ExtensionResources.cs.xlf new file mode 100644 index 0000000000..fa82dcccef --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.Retry/Resources/xlf/ExtensionResources.cs.xlf @@ -0,0 +1,150 @@ + + + + + + Failed to create retries directory due to collisions in '{0}' despite re-trying. + Nepovedlo se vytvoÅ™it adresář opakovaných pokusů kvůli kolizím v adresáři {0}, a to ani pÅ™es opakované pokusy. + '{0}' is the directory where collisions happen + + + Failure threshold policy is enabled, failed tests will not be restarted. + Je povolena zásada prahové hodnoty selhání, neúspěšné testy se nerestartují. + + + + Maximum failed tests threshold is {0} and {1} tests failed + Prahová hodnota maximálního poÄtu neúspěšných testů je {0} a tento poÄet testů selhal: {1}. + + + + Percentage failed threshold is {0}% and {1}% tests failed ({2}/{3}) + Procentuální prahová hodnota selhání je {0} % a toto procento testů selhalo: {1} % ({2}/{3}). + + + + +===================== +Moving last attempt asset files to the default result directory +===================== + + +===================== +PÅ™esouvání souborů prostÅ™edků posledního pokusu do výchozího adresáře výsledků +===================== + + + + + Moving file '{0}' to '{1}' + Soubor {0} se pÅ™esouvá do umístÄ›ní {1}. + + + + Failed to start process '{0}' + Nepovedlo se spustit proces {0}. + + + + Retry failed tests feature allows to restart test execution upon failure. + Funkce opakování neúspěšných testů umožňuje po selhání znovu spustit test. + + + + Retry failed tests + Opakovat neúspěšné testy + + + + Retry failed tests only works with builders of type 'Microsoft.Testing.Platform.Builder.TestApplicationBuilder' + Opakování neúspěšných testů funguje pouze s tvůrci typu Microsoft.Testing.Platform.Builder.TestApplicationBuilder. + + + + Disable retry mechanism if the percentage of failed tests is greater than the specified value + Zakázat mechanismus opakování, pokud je procento neúspěšných testů vÄ›tší než zadaná hodnota + + + + Disable retry mechanism if the number of failed tests is greater than the specified value + Zakázat mechanismus opakování, pokud je poÄet neúspěšných testů vÄ›tší než zadaná hodnota + + + + Retry failed tests feature is not supported in hot reload mode + Funkce opakování neúspěšných testů není v režimu opÄ›tovného naÄítání za provozu podporovaná. + + + + Retry failed tests feature is not supported in server mode + Funkce opakování neúspěšných testů není v režimu serveru podporovaná. + + + + Enable retry failed tests + Povolit opakování neúspěšných testů + + + + Option '{0}' requires option '{1}' to be specified + Možnost {0} vyžaduje, aby byla zadaná možnost {1}. + + + + Option '{0}' expects a single integer argument + Možnost {0} oÄekává jeden celoÄíselný argument. + + + + Options '{0}' and '{1}' cannot be used together + Možnost {0} nelze používat spoleÄnÄ› s možností {1}. + + + + Test host process exited before the retry service could connect to it. Exit code: {0} + Hostitelský proces testu byl ukonÄen dříve, než se k nÄ›mu mohla služba opakování pÅ™ipojit. UkonÄovací kód: {0} + + + + +===================== +Tests suite completed successfully in {0} attempts +===================== + +===================== +Sada testů byla úspěšnÄ› dokonÄena pÅ™i {0} pokusech +===================== + + + + +===================== +Tests suite failed, total failed tests: {0}, exit code: {1}, attempt: {2}/{3} +===================== + + +===================== +Sada testů selhala, celkový poÄet neúspěšných testů: {0}, ukonÄovací kód: {1}, pokus: {2}/{3} +===================== + + + + + +===================== +Tests suite failed in all {0} attempts +===================== + +===================== +Sada testů selhala pÅ™i vÅ¡ech {0} pokusech +===================== + + + + Test suite failed with and exit code different that 2 (failed tests). Failure related to an unexpected condition. Exit code '{0}' + Sada testů neprobÄ›hla úspěšnÄ› a ukonÄovací kód je jiný než 2 (neúspěšné testy). Selhání související s neoÄekávanou podmínkou. UkonÄovací kód: {0} + + + + + \ No newline at end of file diff --git a/src/Platform/Microsoft.Testing.Extensions.Retry/Resources/xlf/ExtensionResources.de.xlf b/src/Platform/Microsoft.Testing.Extensions.Retry/Resources/xlf/ExtensionResources.de.xlf new file mode 100644 index 0000000000..c7e44a2b31 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.Retry/Resources/xlf/ExtensionResources.de.xlf @@ -0,0 +1,150 @@ + + + + + + Failed to create retries directory due to collisions in '{0}' despite re-trying. + Fehler beim Erstellen des Wiederholungsverzeichnisses aufgrund von Konflikten in „{0}“ trotz erneuter Versuche. + '{0}' is the directory where collisions happen + + + Failure threshold policy is enabled, failed tests will not be restarted. + Wenn die Richtlinie für fehlgeschlagene Tests aktiviert ist, werden fehlgeschlagene Tests nicht neu gestartet. + + + + Maximum failed tests threshold is {0} and {1} tests failed + Der Schwellenwert für die maximale Anzahl fehlerhafter Tests ist {0} und {1} Tests mit Fehlern + + + + Percentage failed threshold is {0}% and {1}% tests failed ({2}/{3}) + Der Schwellenwert für den Prozentsatz fehlgeschlagener Tests ist {0} %, und {1} % Tests mit Fehlern ({2}/{3}) + + + + +===================== +Moving last attempt asset files to the default result directory +===================== + + +===================== +Medienobjektdateien des letzten Versuchs werden in das Standardergebnisverzeichnis verschoben. +===================== + + + + + Moving file '{0}' to '{1}' + Die Datei "{0}" wird nach "{1}" verschoben + + + + Failed to start process '{0}' + Fehler beim Starten von Prozess "{0}". + + + + Retry failed tests feature allows to restart test execution upon failure. + Das Feature zum Wiederholen fehlerhafter Tests ermöglicht es, die Testausführung bei einem Fehler neu zu starten. + + + + Retry failed tests + Tests mit Wiederholungsfehlern + + + + Retry failed tests only works with builders of type 'Microsoft.Testing.Platform.Builder.TestApplicationBuilder' + Das Wiederholen fehlerhafter Tests funktioniert nur mit Generatoren vom Typ "Microsoft.Testing.Platform.Builder.TestApplicationBuilder". + + + + Disable retry mechanism if the percentage of failed tests is greater than the specified value + Wiederholungsmechanismus deaktivieren, wenn der Prozentsatz fehlerhafter Tests größer als der angegebene Wert ist + + + + Disable retry mechanism if the number of failed tests is greater than the specified value + Wiederholungsmechanismus deaktivieren, wenn die Anzahl fehlerhafter Tests größer als der angegebene Wert ist + + + + Retry failed tests feature is not supported in hot reload mode + Das Feature zum Wiederholen fehlerhafter Tests wird im Hot Reload-Modus nicht unterstützt + + + + Retry failed tests feature is not supported in server mode + Das Feature zum Wiederholen fehlerhafter Tests wird im Servermodus nicht unterstützt + + + + Enable retry failed tests + Tests mit Wiederholungsfehlern aktivieren + + + + Option '{0}' requires option '{1}' to be specified + Die "{0}"-Option erfordert, dass "{1}" angegeben ist + + + + Option '{0}' expects a single integer argument + Option "{0}" erwartet ein einzelnes ganzzahliges Argument + + + + Options '{0}' and '{1}' cannot be used together + Optionen "{0}" und "{1}" können nicht zusammen verwendet werden + + + + Test host process exited before the retry service could connect to it. Exit code: {0} + Der Testhostprozess wurde beendet, bevor der Wiederholungsdienst eine Verbindung herstellen konnte. Exitcode: {0} + + + + +===================== +Tests suite completed successfully in {0} attempts +===================== + +===================== +Die Testsammlung wurde in {0} Versuchen erfolgreich abgeschlossen. +===================== + + + + +===================== +Tests suite failed, total failed tests: {0}, exit code: {1}, attempt: {2}/{3} +===================== + + +===================== +Fehler bei der Testsammlung. Fehlerhafte Tests gesamt: {0}, Exitcode: {1}, Versuch: {2}/{3} +===================== + + + + + +===================== +Tests suite failed in all {0} attempts +===================== + +===================== +Fehler bei der Testsammlung bei allen {0} Versuchen. +===================== + + + + Test suite failed with and exit code different that 2 (failed tests). Failure related to an unexpected condition. Exit code '{0}' + Bei der Testsammlung ist ein Fehler aufgetreten, und der Exitcode unterscheidet sich von 2 (fehlgeschlagene Tests). Fehler im Zusammenhang mit einer unerwarteten Bedingung. Exitcode "{0}" + + + + + \ No newline at end of file diff --git a/src/Platform/Microsoft.Testing.Extensions.Retry/Resources/xlf/ExtensionResources.es.xlf b/src/Platform/Microsoft.Testing.Extensions.Retry/Resources/xlf/ExtensionResources.es.xlf new file mode 100644 index 0000000000..5e008904ad --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.Retry/Resources/xlf/ExtensionResources.es.xlf @@ -0,0 +1,151 @@ + + + + + + Failed to create retries directory due to collisions in '{0}' despite re-trying. + No se pudo crear el directorio de reintentos debido a colisiones en '{0}' a pesar de volver a intentarlo. + '{0}' is the directory where collisions happen + + + Failure threshold policy is enabled, failed tests will not be restarted. + La directiva de umbral de errores está habilitada; no se reiniciarán las pruebas con errores. + + + + Maximum failed tests threshold is {0} and {1} tests failed + El umbral máximo de pruebas con errores es {0} y {1} pruebas erróneas + + + + Percentage failed threshold is {0}% and {1}% tests failed ({2}/{3}) + El umbral de porcentaje de errores es {0}% y {1}% de pruebas no superadas ({2}/{3}) + + + + +===================== +Moving last attempt asset files to the default result directory +===================== + + +======================== +Moviendo los archivos de recursos del último intento al directorio de resultados predeterminado +======================== + + + + + Moving file '{0}' to '{1}' + Moviendo el archivo '{0}' a '{1}' + + + + Failed to start process '{0}' + No se pudo iniciar el proceso '{0}' + + + + Retry failed tests feature allows to restart test execution upon failure. + La característica Reintentar pruebas con errores permite reiniciar la ejecución de pruebas en caso de error. + + + + Retry failed tests + Reintentar pruebas con errores + + + + Retry failed tests only works with builders of type 'Microsoft.Testing.Platform.Builder.TestApplicationBuilder' + Reintentar pruebas con errores solo funciona con generadores de tipo "Microsoft.Testing.Platform.Builder.TestApplicationBuilder" + + + + Disable retry mechanism if the percentage of failed tests is greater than the specified value + Deshabilitar el mecanismo de reintento si el porcentaje de pruebas con errores es mayor que el valor especificado + + + + Disable retry mechanism if the number of failed tests is greater than the specified value + Deshabilitar el mecanismo de reintento si el número de pruebas con errores es mayor que el valor especificado + + + + Retry failed tests feature is not supported in hot reload mode + La característica Reintentar pruebas con errores no se admite en el modo de recarga activa + + + + Retry failed tests feature is not supported in server mode + La característica reintentar pruebas con errores no se admite en el modo de servidor + + + + Enable retry failed tests + Habilitar pruebas con errores de reintento + + + + Option '{0}' requires option '{1}' to be specified + La opción '{0}' requiere que se especifique la opción '{1}' + + + + Option '{0}' expects a single integer argument + La opción '{0}' espera un único argumento entero + + + + Options '{0}' and '{1}' cannot be used together + Las opciones '{0}' y '{1}' no se pueden usar juntas + + + + Test host process exited before the retry service could connect to it. Exit code: {0} + El proceso de host de prueba se cerró antes de que el servicio de reintento pudiera conectarse a él. Código de salida: {0} + + + + +===================== +Tests suite completed successfully in {0} attempts +===================== + +======================== +El conjunto de pruebas se completó correctamente en {0} intentos +===================== + + + + +===================== +Tests suite failed, total failed tests: {0}, exit code: {1}, attempt: {2}/{3} +===================== + + +======================== +Error del conjunto de pruebas, total de pruebas erróneas: {0}, código de salida: {1}, intento: {2}/{3} +======================== + + + + + +===================== +Tests suite failed in all {0} attempts +===================== + +======================== +Error del conjunto de pruebas en todos los + intentos{0} +===================== + + + + Test suite failed with and exit code different that 2 (failed tests). Failure related to an unexpected condition. Exit code '{0}' + Error del conjunto de pruebas con el código de salida distinto de 2 (pruebas con error). Error relacionado con una condición inesperada. Código de salida ''{0}'' + + + + + \ No newline at end of file diff --git a/src/Platform/Microsoft.Testing.Extensions.Retry/Resources/xlf/ExtensionResources.fr.xlf b/src/Platform/Microsoft.Testing.Extensions.Retry/Resources/xlf/ExtensionResources.fr.xlf new file mode 100644 index 0000000000..debdb1c84f --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.Retry/Resources/xlf/ExtensionResources.fr.xlf @@ -0,0 +1,150 @@ + + + + + + Failed to create retries directory due to collisions in '{0}' despite re-trying. + Échec de la création du répertoire des nouvelles tentatives en raison de collisions dans « {0} » malgré une nouvelle tentative. + '{0}' is the directory where collisions happen + + + Failure threshold policy is enabled, failed tests will not be restarted. + La stratégie de seuil d’échec est activée, les tests ayant échoué ne seront pas redémarrés. + + + + Maximum failed tests threshold is {0} and {1} tests failed + Le seuil maximal des tests ayant échoué est {0} et {1} tests ont échoué + + + + Percentage failed threshold is {0}% and {1}% tests failed ({2}/{3}) + Le seuil de pourcentage d’échec est {0}% et {1}% de tests ont échoué ({2}/{3}) + + + + +===================== +Moving last attempt asset files to the default result directory +===================== + + +===================== +Déplacement des fichiers de ressources de la dernière tentative vers le répertoire de résultats par défaut +===================== + + + + + Moving file '{0}' to '{1}' + Déplacement du fichier '{0}' vers '{1}' + + + + Failed to start process '{0}' + Échec de démarrage du processus « {0} » + + + + Retry failed tests feature allows to restart test execution upon failure. + La fonctionnalité de nouvelles tentatives de tests ayant échoué permet de redémarrer l’exécution des tests en cas d’échec. + + + + Retry failed tests + Nouvelle tentative de tests ayant échoué + + + + Retry failed tests only works with builders of type 'Microsoft.Testing.Platform.Builder.TestApplicationBuilder' + Les nouvelles tentatives de tests ayant échoué fonctionnent uniquement avec les générateurs de type « Microsoft.Testing.Platform.Builder.TestApplicationBuilder » + + + + Disable retry mechanism if the percentage of failed tests is greater than the specified value + Désactiver le mécanisme de nouvelle tentative si le pourcentage de tests ayant échoué est supérieur à la valeur spécifiée + + + + Disable retry mechanism if the number of failed tests is greater than the specified value + Désactiver le mécanisme de nouvelle tentative si le nombre de tests ayant échoué est supérieur à la valeur spécifiée + + + + Retry failed tests feature is not supported in hot reload mode + La fonctionnalité de nouvelles tentatives de tests ayant échoué n’est pas prise en charge en mode rechargement à chaud + + + + Retry failed tests feature is not supported in server mode + La fonctionnalité de nouvelles tentatives de tests ayant échoué n’est pas prise en charge en mode serveur + + + + Enable retry failed tests + Activer les nouvelles tentatives de tests ayant échoué + + + + Option '{0}' requires option '{1}' to be specified + L’option '{0}' nécessite la spécification de l’option '{1}' + + + + Option '{0}' expects a single integer argument + L’option '{0}' attend un seul argument entier + + + + Options '{0}' and '{1}' cannot be used together + Les options «{0}» et «{1}» ne peuvent pas être utilisées ensemble + + + + Test host process exited before the retry service could connect to it. Exit code: {0} + Le processus hôte de test s’est arrêté avant que le service de nouvelle tentative puisse s’y connecter. Code de sortie : {0} + + + + +===================== +Tests suite completed successfully in {0} attempts +===================== + +===================== +La suite de tests s’est terminée correctement dans les {0} tentatives +===================== + + + + +===================== +Tests suite failed, total failed tests: {0}, exit code: {1}, attempt: {2}/{3} +===================== + + +===================== +Échec de la suite de tests, nombre total de tests ayant échoué : {0}, code de sortie : {1}, tentative : {2}/{3} +===================== + + + + + +===================== +Tests suite failed in all {0} attempts +===================== + +===================== +Échec de la suite de tests dans toutes les {0} tentatives +===================== + + + + Test suite failed with and exit code different that 2 (failed tests). Failure related to an unexpected condition. Exit code '{0}' + La suite de tests a échoué avec un code de sortie différent de 2 (tests échoués). Défaillance liée à une condition inattendue. Code de sortie « {0} » + + + + + \ No newline at end of file diff --git a/src/Platform/Microsoft.Testing.Extensions.Retry/Resources/xlf/ExtensionResources.it.xlf b/src/Platform/Microsoft.Testing.Extensions.Retry/Resources/xlf/ExtensionResources.it.xlf new file mode 100644 index 0000000000..18d2389948 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.Retry/Resources/xlf/ExtensionResources.it.xlf @@ -0,0 +1,150 @@ + + + + + + Failed to create retries directory due to collisions in '{0}' despite re-trying. + Non è possibile creare la directory dei tentativi a causa di collisioni in “{0}†nonostante i tentativi ripetuti. + '{0}' is the directory where collisions happen + + + Failure threshold policy is enabled, failed tests will not be restarted. + Se il criterio della soglia di errore è abilitato, i test non riusciti non verranno riavviati. + + + + Maximum failed tests threshold is {0} and {1} tests failed + La soglia massima dei test non superati è {0} e i test {1} non sono riusciti + + + + Percentage failed threshold is {0}% and {1}% tests failed ({2}/{3}) + La soglia percentuale di operazioni non riuscite è del {0}% e del {1}% di test non riusciti ({2}/{3}) + + + + +===================== +Moving last attempt asset files to the default result directory +===================== + + +===================== +Spostamento dei file di asset dell'ultimo tentativo nella directory dei risultati predefinita +===================== + + + + + Moving file '{0}' to '{1}' + Spostamento del file '{0}' in '{1}' + + + + Failed to start process '{0}' + Impossibile avviare il processo '{0}' + + + + Retry failed tests feature allows to restart test execution upon failure. + La funzionalità di ripetizione dei test non riusciti consente di riavviare l'esecuzione dei test in caso di errore. + + + + Retry failed tests + Ripeti i test non riusciti + + + + Retry failed tests only works with builders of type 'Microsoft.Testing.Platform.Builder.TestApplicationBuilder' + La ripetizione dei test non riusciti funziona solo con i generatori di tipo 'Microsoft.Testing.Platform.Builder.TestApplicationBuilder'. + + + + Disable retry mechanism if the percentage of failed tests is greater than the specified value + Disabilita il meccanismo di ripetizione dei tentativi se la percentuale di test non riusciti è maggiore del valore specificato. + + + + Disable retry mechanism if the number of failed tests is greater than the specified value + Disabilita il meccanismo di ripetizione dei tentativi se il numero di test non riusciti è maggiore del valore specificato. + + + + Retry failed tests feature is not supported in hot reload mode + La funzionalità di ripetizione dei test non riusciti non è supportata nella modalità di ricaricamento rapido. + + + + Retry failed tests feature is not supported in server mode + La funzionalità di ripetizione dei test non riusciti non è supportata in modalità server + + + + Enable retry failed tests + Abilita la ripetizione dei test non riusciti + + + + Option '{0}' requires option '{1}' to be specified + L'opzione '{0}' richiede l'opzione '{1}' da specificare + + + + Option '{0}' expects a single integer argument + L'opzione '{0}' prevede un singolo argomento integer + + + + Options '{0}' and '{1}' cannot be used together + Le opzioni '{0}' e '{1}' non possono essere usate insieme + + + + Test host process exited before the retry service could connect to it. Exit code: {0} + Il processo host di test è stato chiuso prima che il servizio di ripetizione potesse connettersi ad esso. Codice di uscita: {0} + + + + +===================== +Tests suite completed successfully in {0} attempts +===================== + +===================== +Il gruppo di test è stato completato correttamente in {0} tentativi +===================== + + + + +===================== +Tests suite failed, total failed tests: {0}, exit code: {1}, attempt: {2}/{3} +===================== + + +===================== +Gruppo di test non riuscito, totale test non superati: {0}, codice di uscita: {1}, tentativo: {2}/{3} +===================== + + + + + +===================== +Tests suite failed in all {0} attempts +===================== + +===================== +Gruppo di test non riuscito in tutti i {0} tentativi +===================== + + + + Test suite failed with and exit code different that 2 (failed tests). Failure related to an unexpected condition. Exit code '{0}' + Il gruppo di test non è riuscito e il codice di uscita è diverso da 2 (test non superati). Errore correlato a una condizione imprevista. Codice di uscita "{0}" + + + + + \ No newline at end of file diff --git a/src/Platform/Microsoft.Testing.Extensions.Retry/Resources/xlf/ExtensionResources.ja.xlf b/src/Platform/Microsoft.Testing.Extensions.Retry/Resources/xlf/ExtensionResources.ja.xlf new file mode 100644 index 0000000000..1e003faeed --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.Retry/Resources/xlf/ExtensionResources.ja.xlf @@ -0,0 +1,150 @@ + + + + + + Failed to create retries directory due to collisions in '{0}' despite re-trying. + å†è©¦è¡Œä¸­ã« '{0}' ã§ç«¶åˆãŒç™ºç”Ÿã—ãŸãŸã‚ã€å†è©¦è¡Œãƒ‡ã‚£ãƒ¬ã‚¯ãƒˆãƒªã‚’作æˆã§ãã¾ã›ã‚“ã§ã—ãŸã€‚ + '{0}' is the directory where collisions happen + + + Failure threshold policy is enabled, failed tests will not be restarted. + 失敗ã—ãã„値ãƒãƒªã‚·ãƒ¼ãŒæœ‰åŠ¹ã«ãªã£ã¦ã„ã‚‹ãŸã‚ã€å¤±æ•—ã—ãŸãƒ†ã‚¹ãƒˆã¯å†é–‹ã•ã‚Œã¾ã›ã‚“。 + + + + Maximum failed tests threshold is {0} and {1} tests failed + 失敗ã—ãŸãƒ†ã‚¹ãƒˆã®æœ€å¤§ã—ãã„値㯠{0} ã§ã€{1} ã®ãƒ†ã‚¹ãƒˆãŒå¤±æ•—ã—ã¾ã—㟠+ + + + Percentage failed threshold is {0}% and {1}% tests failed ({2}/{3}) + 失敗率ã®ã—ãã„値㯠{0} % ã§ã€å¤±æ•—ã—ãŸãƒ†ã‚¹ãƒˆã¯ {1} % ã§ã™ ({2}/{3}) + + + + +===================== +Moving last attempt asset files to the default result directory +===================== + + +===================== +最後ã®è©¦è¡Œè³‡ç”£ãƒ•ã‚¡ã‚¤ãƒ«ã‚’既定ã®çµæžœãƒ‡ã‚£ãƒ¬ã‚¯ãƒˆãƒªã«ç§»å‹•ã—ã¦ã„ã¾ã™ +===================== + + + + + Moving file '{0}' to '{1}' + ファイル '{0}' ã‚’ '{1}' ã«ç§»å‹•ã—ã¦ã„ã¾ã™ + + + + Failed to start process '{0}' + å‡¦ç† '{0}' を開始ã§ãã¾ã›ã‚“ã§ã—㟠+ + + + Retry failed tests feature allows to restart test execution upon failure. + 失敗ã—ãŸãƒ†ã‚¹ãƒˆã®å†è©¦è¡Œæ©Ÿèƒ½ã‚’使用ã™ã‚‹ã¨ã€å¤±æ•—時ã«ãƒ†ã‚¹ãƒˆã®å®Ÿè¡Œã‚’å†é–‹ã§ãã¾ã™ã€‚ + + + + Retry failed tests + 失敗ã—ãŸãƒ†ã‚¹ãƒˆã®å†è©¦è¡Œ + + + + Retry failed tests only works with builders of type 'Microsoft.Testing.Platform.Builder.TestApplicationBuilder' + 失敗ã—ãŸãƒ†ã‚¹ãƒˆã®å†è©¦è¡Œã¯ã€'Microsoft.Testing.Platform.Builder.TestApplicationBuilder' åž‹ã®ãƒ“ルダーã§ã®ã¿æ©Ÿèƒ½ã—ã¾ã™ + + + + Disable retry mechanism if the percentage of failed tests is greater than the specified value + 失敗ã—ãŸãƒ†ã‚¹ãƒˆã®å‰²åˆãŒæŒ‡å®šã—ãŸå€¤ã‚’超ãˆã‚‹å ´åˆã¯å†è©¦è¡Œãƒ¡ã‚«ãƒ‹ã‚ºãƒ ã‚’無効ã«ã™ã‚‹ + + + + Disable retry mechanism if the number of failed tests is greater than the specified value + 失敗ã—ãŸãƒ†ã‚¹ãƒˆã®æ•°ãŒæŒ‡å®šã—ãŸå€¤ã‚’超ãˆã‚‹å ´åˆã¯å†è©¦è¡Œãƒ¡ã‚«ãƒ‹ã‚ºãƒ ã‚’無効ã«ã™ã‚‹ + + + + Retry failed tests feature is not supported in hot reload mode + 失敗ã—ãŸãƒ†ã‚¹ãƒˆã®å†è©¦è¡Œæ©Ÿèƒ½ã¯ã€ãƒ›ãƒƒãƒˆ リロード モードã§ã¯ã‚µãƒãƒ¼ãƒˆã•ã‚Œã¦ã„ã¾ã›ã‚“ + + + + Retry failed tests feature is not supported in server mode + 失敗ã—ãŸãƒ†ã‚¹ãƒˆã®å†è©¦è¡Œæ©Ÿèƒ½ã¯ã€ã‚µãƒ¼ãƒãƒ¼ モードã§ã¯ã‚µãƒãƒ¼ãƒˆã•ã‚Œã¦ã„ã¾ã›ã‚“ + + + + Enable retry failed tests + 失敗ã—ãŸãƒ†ã‚¹ãƒˆã®å†è©¦è¡Œã‚’有効ã«ã™ã‚‹ + + + + Option '{0}' requires option '{1}' to be specified + オプション '{0}' ã§ã¯ã€ã‚ªãƒ—ション '{1}' を指定ã™ã‚‹å¿…è¦ãŒã‚ã‚Šã¾ã™ + + + + Option '{0}' expects a single integer argument + オプション '{0}' ã«ã¯ 1 ã¤ã®æ•´æ•°å¼•æ•°ãŒå¿…è¦ã§ã™ + + + + Options '{0}' and '{1}' cannot be used together + オプション '{0}' 㨠'{1}' を一緒ã«ä½¿ç”¨ã™ã‚‹ã“ã¨ã¯ã§ãã¾ã›ã‚“ + + + + Test host process exited before the retry service could connect to it. Exit code: {0} + å†è©¦è¡Œã‚µãƒ¼ãƒ“スãŒæŽ¥ç¶šã™ã‚‹å‰ã«ã€ãƒ†ã‚¹ãƒˆ ホスト プロセスãŒçµ‚了ã—ã¾ã—ãŸã€‚終了コード: {0} + + + + +===================== +Tests suite completed successfully in {0} attempts +===================== + +===================== +テスト スイート㌠{0} 回ã®è©¦è¡Œã§æ­£å¸¸ã«å®Œäº†ã—ã¾ã—㟠+===================== + + + + +===================== +Tests suite failed, total failed tests: {0}, exit code: {1}, attempt: {2}/{3} +===================== + + +===================== +テスト スイートãŒå¤±æ•—ã—ã¾ã—ãŸã€‚失敗ã—ãŸãƒ†ã‚¹ãƒˆã®åˆè¨ˆæ•°: {0}ã€çµ‚了コード: {1}ã€è©¦è¡Œ: {2}/{3} +===================== + + + + + +===================== +Tests suite failed in all {0} attempts +===================== + +===================== +{0} 回ã®è©¦è¡Œã™ã¹ã¦ã§ãƒ†ã‚¹ãƒˆ スイートãŒå¤±æ•—ã—ã¾ã—㟠+===================== + + + + Test suite failed with and exit code different that 2 (failed tests). Failure related to an unexpected condition. Exit code '{0}' + テスト スイートãŒå¤±æ•—ã—ã€çµ‚了コード㌠2 (失敗ã—ãŸãƒ†ã‚¹ãƒˆ) ã¨ç•°ãªã‚Šã¾ã—ãŸã€‚予期ã—ãªã„状態ã«é–¢é€£ã™ã‚‹ã‚¨ãƒ©ãƒ¼ã§ã™ã€‚終了コード '{0}' + + + + + \ No newline at end of file diff --git a/src/Platform/Microsoft.Testing.Extensions.Retry/Resources/xlf/ExtensionResources.ko.xlf b/src/Platform/Microsoft.Testing.Extensions.Retry/Resources/xlf/ExtensionResources.ko.xlf new file mode 100644 index 0000000000..d521ae9495 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.Retry/Resources/xlf/ExtensionResources.ko.xlf @@ -0,0 +1,150 @@ + + + + + + Failed to create retries directory due to collisions in '{0}' despite re-trying. + 다시 ì‹œë„ì—ë„ ë¶ˆêµ¬í•˜ê³  '{0}'ì˜ ì¶©ëŒë¡œ ì¸í•´ ìž¬ì‹œë„ ë””ë ‰í„°ë¦¬ë¥¼ 만들지 못했습니다. + '{0}' is the directory where collisions happen + + + Failure threshold policy is enabled, failed tests will not be restarted. + 실패 임계값 ì •ì±…ì„ ì‚¬ìš©í•˜ë„ë¡ ì„¤ì •í–ˆìŠµë‹ˆë‹¤. 실패한 테스트는 다시 시작ë˜ì§€ 않습니다. + + + + Maximum failed tests threshold is {0} and {1} tests failed + 실패한 최대 테스트 ìž„ê³„ê°’ì´ {0}ì´ê³  {1} 테스트가 실패했습니다. + + + + Percentage failed threshold is {0}% and {1}% tests failed ({2}/{3}) + 실패한 임계값 ë°±ë¶„ìœ¨ì€ {0}%ì´ë©° {1}% í…ŒìŠ¤íŠ¸ì— ì‹¤íŒ¨í–ˆìŠµë‹ˆë‹¤({2}/{3}). + + + + +===================== +Moving last attempt asset files to the default result directory +===================== + + +===================== +마지막 ì‹œë„ ìžì‚° 파ì¼ì„ 기본 ê²°ê³¼ 디렉터리로 ì´ë™ +===================== + + + + + Moving file '{0}' to '{1}' + íŒŒì¼ {0}ì„(를) {1}(으)ë¡œ ì´ë™ + + + + Failed to start process '{0}' + 프로세스 '{0}'ì„(를) 시작하지 못했습니다. + + + + Retry failed tests feature allows to restart test execution upon failure. + 실패한 테스트 다시 ì‹œë„ ê¸°ëŠ¥ì„ ì‚¬ìš©í•˜ë©´ 실패 ì‹œ 테스트 ì‹¤í–‰ì„ ë‹¤ì‹œ 시작할 수 있습니다. + + + + Retry failed tests + 실패한 테스트 다시 ì‹œë„ + + + + Retry failed tests only works with builders of type 'Microsoft.Testing.Platform.Builder.TestApplicationBuilder' + 실패한 테스트 다시 ì‹œë„는 'Microsoft.Testing.Platform.Builder.TestApplicationBuilder' ìœ í˜•ì˜ ìž‘ì„±ê¸°ì—서만 ìž‘ë™í•©ë‹ˆë‹¤. + + + + Disable retry mechanism if the percentage of failed tests is greater than the specified value + 실패한 í…ŒìŠ¤íŠ¸ì˜ ë¹„ìœ¨ì´ ì§€ì •ëœ ê°’ë³´ë‹¤ í° ê²½ìš° 다시 ì‹œë„ ë©”ì»¤ë‹ˆì¦˜ì„ ì‚¬ìš©í•˜ì§€ ì•Šë„ë¡ ì„¤ì • + + + + Disable retry mechanism if the number of failed tests is greater than the specified value + 실패한 테스트 수가 ì§€ì •ëœ ê°’ë³´ë‹¤ í° ê²½ìš° 다시 ì‹œë„ ë©”ì»¤ë‹ˆì¦˜ì„ ì‚¬ìš©í•˜ì§€ ì•Šë„ë¡ ì„¤ì • + + + + Retry failed tests feature is not supported in hot reload mode + 실패한 테스트 다시 ì‹œë„ ê¸°ëŠ¥ì€ í•« 다시 로드 모드ì—ì„œ 지ì›ë˜ì§€ 않습니다. + + + + Retry failed tests feature is not supported in server mode + 실패한 테스트 다시 ì‹œë„ ê¸°ëŠ¥ì€ ì„œë²„ 모드ì—ì„œ 지ì›ë˜ì§€ 않습니다. + + + + Enable retry failed tests + 실패한 테스트 다시 ì‹œë„ ì‚¬ìš© + + + + Option '{0}' requires option '{1}' to be specified + '{0}' 옵션ì„(를) 사용하려면 '{1}' ì˜µì…˜ì„ ì§€ì •í•´ì•¼ 합니다. + + + + Option '{0}' expects a single integer argument + '{0}' 옵션ì—는 ë‹¨ì¼ ì •ìˆ˜ ì¸ìˆ˜ê°€ 필요합니다. + + + + Options '{0}' and '{1}' cannot be used together + '{0}' ë° '{1}' ì˜µì…˜ì€ í•¨ê»˜ 사용할 수 없습니다. + + + + Test host process exited before the retry service could connect to it. Exit code: {0} + 다시 ì‹œë„ ì„œë¹„ìŠ¤ê°€ ì—°ê²°ë˜ê¸° ì „ì— í…ŒìŠ¤íŠ¸ 호스트 프로세스가 종료ë˜ì—ˆìŠµë‹ˆë‹¤. 종료 코드: {0} + + + + +===================== +Tests suite completed successfully in {0} attempts +===================== + +===================== +테스트 ë„구 모ìŒì´ {0} ì‹œë„ì—ì„œ 완료ë˜ì—ˆìŠµë‹ˆë‹¤. +===================== + + + + +===================== +Tests suite failed, total failed tests: {0}, exit code: {1}, attempt: {2}/{3} +===================== + + +===================== +테스트 ë„구 모ìŒì´ 실패했습니다. 실패한 ì´ í…ŒìŠ¤íŠ¸: {0}, 종료 코드: {1}, ì‹œë„: {2}/{3} +===================== + + + + + +===================== +Tests suite failed in all {0} attempts +===================== + +===================== +테스트 ë„구 모ìŒì´ 모든 {0} ì‹œë„ì— ì‹¤íŒ¨í–ˆìŠµë‹ˆë‹¤ +===================== + + + + Test suite failed with and exit code different that 2 (failed tests). Failure related to an unexpected condition. Exit code '{0}' + 테스트 ë„구 모ìŒì´ 2와 다른 종료 코드를 사용하여 실패했습니다(테스트 실패). 예기치 ì•Šì€ ì¡°ê±´ê³¼ ê´€ë ¨ëœ ì˜¤ë¥˜ìž…ë‹ˆë‹¤. 종료 코드 '{0}' + + + + + \ No newline at end of file diff --git a/src/Platform/Microsoft.Testing.Extensions.Retry/Resources/xlf/ExtensionResources.pl.xlf b/src/Platform/Microsoft.Testing.Extensions.Retry/Resources/xlf/ExtensionResources.pl.xlf new file mode 100644 index 0000000000..a9d22ccaa5 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.Retry/Resources/xlf/ExtensionResources.pl.xlf @@ -0,0 +1,150 @@ + + + + + + Failed to create retries directory due to collisions in '{0}' despite re-trying. + Nie można utworzyć katalogu ponownych prób z powodu kolizji w katalogu „{0}†mimo ponownej próby. + '{0}' is the directory where collisions happen + + + Failure threshold policy is enabled, failed tests will not be restarted. + Zasady progu bÅ‚Ä™dów sÄ… wÅ‚Ä…czone, a testy zakoÅ„czone niepowodzeniem nie zostanÄ… ponownie uruchomione. + + + + Maximum failed tests threshold is {0} and {1} tests failed + Próg maksymalnej liczby testów zakoÅ„czonych niepowodzeniem to {0} i liczba testów zakoÅ„czonych niepowodzeniem wyniosÅ‚a {1} + + + + Percentage failed threshold is {0}% and {1}% tests failed ({2}/{3}) + Wartość procentowa progu niepowodzenia wynosi {0}% i {1}% testów nie powiodÅ‚o siÄ™ ({2}/{3}) + + + + +===================== +Moving last attempt asset files to the default result directory +===================== + + +===================== +Przeniesienie plików zasobów ostatniej próby do domyÅ›lnego katalogu wyników +===================== + + + + + Moving file '{0}' to '{1}' + Przenoszenie pliku „{0}†do {1} + + + + Failed to start process '{0}' + Nie można uruchomić procesu „{0}†+ + + + Retry failed tests feature allows to restart test execution upon failure. + Funkcja ponawiania testów zakoÅ„czonych niepowodzeniem umożliwia ponowne uruchomienie wykonywania testu po niepowodzeniu. + + + + Retry failed tests + Ponów próbÄ™ testów zakoÅ„czonych niepowodzeniem + + + + Retry failed tests only works with builders of type 'Microsoft.Testing.Platform.Builder.TestApplicationBuilder' + Ponawianie testów zakoÅ„czonych niepowodzeniem dziaÅ‚a tylko z konstruktorami typu „Microsoft.Testing.Platform.Builder.TestApplicationBuilder†+ + + + Disable retry mechanism if the percentage of failed tests is greater than the specified value + WyÅ‚Ä…cz mechanizm ponawiania prób, jeÅ›li procent testów zakoÅ„czonych niepowodzeniem jest wiÄ™kszy niż okreÅ›lona wartość + + + + Disable retry mechanism if the number of failed tests is greater than the specified value + WyÅ‚Ä…cz mechanizm ponawiania prób, jeÅ›li liczba testów zakoÅ„czonych niepowodzeniem jest wiÄ™ksza niż okreÅ›lona wartość + + + + Retry failed tests feature is not supported in hot reload mode + Funkcja ponownych testów zakoÅ„czonych niepowodzeniem nie jest obsÅ‚ugiwana w trybie ponownego Å‚adowania na gorÄ…co + + + + Retry failed tests feature is not supported in server mode + Funkcja ponawiania testów zakoÅ„czonych niepowodzeniem nie jest obsÅ‚ugiwana w trybie serwera + + + + Enable retry failed tests + WÅ‚Ä…cz testy zakoÅ„czone niepowodzeniem ponowieÅ„ + + + + Option '{0}' requires option '{1}' to be specified + Opcja „{0}†wymaga okreÅ›lenia opcji „{1}†+ + + + Option '{0}' expects a single integer argument + Opcja „{0}†oczekuje argumentu pojedynczej liczby caÅ‚kowitej + + + + Options '{0}' and '{1}' cannot be used together + Opcji „{0}†i „{1}†nie można używać razem + + + + Test host process exited before the retry service could connect to it. Exit code: {0} + Proces hosta testowego zakoÅ„czyÅ‚ siÄ™, zanim usÅ‚uga ponawiania próby mogÅ‚a nawiÄ…zać z nim poÅ‚Ä…czenie. Kod zakoÅ„czenia: {0} + + + + +===================== +Tests suite completed successfully in {0} attempts +===================== + +===================== +PomyÅ›lnie ukoÅ„czono zestaw testów w {0} próbach +===================== + + + + +===================== +Tests suite failed, total failed tests: {0}, exit code: {1}, attempt: {2}/{3} +===================== + + +===================== +Zestaw testów nie powiódÅ‚ siÄ™, Å‚Ä…czna liczba testów zakoÅ„czonych niepowodzeniem: {0}, kod zakoÅ„czenia: {1}, próba: {2}/{3} +===================== + + + + + +===================== +Tests suite failed in all {0} attempts +===================== + +===================== +Zestaw testów nie powiódÅ‚ siÄ™ we wszystkich {0} próbach +===================== + + + + Test suite failed with and exit code different that 2 (failed tests). Failure related to an unexpected condition. Exit code '{0}' + Zestaw testów nie powiódÅ‚ siÄ™. Kod zakoÅ„czenia jest inny niż 2 (testy zakoÅ„czone niepowodzeniem). BÅ‚Ä…d zwiÄ…zany z nieoczekiwanym warunkiem. Kod zakoÅ„czenia „{0}†+ + + + + \ No newline at end of file diff --git a/src/Platform/Microsoft.Testing.Extensions.Retry/Resources/xlf/ExtensionResources.pt-BR.xlf b/src/Platform/Microsoft.Testing.Extensions.Retry/Resources/xlf/ExtensionResources.pt-BR.xlf new file mode 100644 index 0000000000..48006921c3 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.Retry/Resources/xlf/ExtensionResources.pt-BR.xlf @@ -0,0 +1,150 @@ + + + + + + Failed to create retries directory due to collisions in '{0}' despite re-trying. + Falha ao criar o diretório de novas tentativas devido a colisões em '{0}', apesar das novas tentativas. + '{0}' is the directory where collisions happen + + + Failure threshold policy is enabled, failed tests will not be restarted. + A política de limite de falha está habilitada, os testes com falha não serão reiniciados. + + + + Maximum failed tests threshold is {0} and {1} tests failed + O limite máximo de testes com falha é {0} e {1} testes falharam + + + + Percentage failed threshold is {0}% and {1}% tests failed ({2}/{3}) + O limite de porcentagem com falha é {0}% e {1}% dos testes falharam ({2}/{3}) + + + + +===================== +Moving last attempt asset files to the default result directory +===================== + + +===================== +Movendo arquivos de ativo da última tentativa para o diretório de resultados padrão +===================== + + + + + Moving file '{0}' to '{1}' + Movendo o arquivo ''{0}'' para ''{1}'' + + + + Failed to start process '{0}' + Falha ao iniciar o processo '{0}' + + + + Retry failed tests feature allows to restart test execution upon failure. + O recurso repetir testes com falha permite reiniciar a execução de teste após falha. + + + + Retry failed tests + Repetir testes com falha + + + + Retry failed tests only works with builders of type 'Microsoft.Testing.Platform.Builder.TestApplicationBuilder' + A repetição de testes com falha funciona somente com construtores do tipo ''Microsoft.Testing.Platform.Builder.TestApplicationBuilder'' + + + + Disable retry mechanism if the percentage of failed tests is greater than the specified value + Desabilitar o mecanismo de repetição se a porcentagem de testes com falha for maior que o valor especificado + + + + Disable retry mechanism if the number of failed tests is greater than the specified value + Desabilitar o mecanismo de repetição se o número de testes com falha for maior que o valor especificado + + + + Retry failed tests feature is not supported in hot reload mode + Não há suporte para o recurso de repetição de testes com falha no modo de recarga dinâmica + + + + Retry failed tests feature is not supported in server mode + Não há suporte para o recurso de repetição de testes com falha no modo de servidor + + + + Enable retry failed tests + Habilitar repetição de testes com falha + + + + Option '{0}' requires option '{1}' to be specified + A opção ''{0}'' requer que a opção ''{1}'' seja especificada + + + + Option '{0}' expects a single integer argument + A opção ''{0}'' espera um único argumento inteiro + + + + Options '{0}' and '{1}' cannot be used together + As opções ''{0}'' e ''{1}'' não podem ser usadas juntas + + + + Test host process exited before the retry service could connect to it. Exit code: {0} + O processo de host de teste foi encerrado antes que o serviço de repetição pudesse se conectar a ele. Código de saída: {0} + + + + +===================== +Tests suite completed successfully in {0} attempts +===================== + +===================== +Pacote de testes concluído com êxito em {0} tentativas +===================== + + + + +===================== +Tests suite failed, total failed tests: {0}, exit code: {1}, attempt: {2}/{3} +===================== + + +===================== +Falha no pacote de testes, total de testes com falha: {0}, código de saída: {1}, tentativa: {2}/{3} +===================== + + + + + +===================== +Tests suite failed in all {0} attempts +===================== + +===================== +Falha no pacote de testes em todas as {0} tentativas +===================== + + + + Test suite failed with and exit code different that 2 (failed tests). Failure related to an unexpected condition. Exit code '{0}' + Falha no conjunto de testes e código de saída diferente de 2 (testes com falha). Falha relacionada a uma condição inesperada. Código de saída "{0}" + + + + + \ No newline at end of file diff --git a/src/Platform/Microsoft.Testing.Extensions.Retry/Resources/xlf/ExtensionResources.ru.xlf b/src/Platform/Microsoft.Testing.Extensions.Retry/Resources/xlf/ExtensionResources.ru.xlf new file mode 100644 index 0000000000..31fb110066 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.Retry/Resources/xlf/ExtensionResources.ru.xlf @@ -0,0 +1,150 @@ + + + + + + Failed to create retries directory due to collisions in '{0}' despite re-trying. + Ðе удалоÑÑŒ Ñоздать каталог повторов из-за конфликтов в {0}, неÑÐ¼Ð¾Ñ‚Ñ€Ñ Ð½Ð° повторную попытку. + '{0}' is the directory where collisions happen + + + Failure threshold policy is enabled, failed tests will not be restarted. + Политика порога отказа включена, неудачные теÑÑ‚Ñ‹ не будут перезапущены. + + + + Maximum failed tests threshold is {0} and {1} tests failed + МакÑимальное пороговое значение Ð´Ð»Ñ Ð½ÐµÑƒÐ´Ð°Ñ‡Ð½Ñ‹Ñ… теÑтов — {0}, и неудачных теÑтов — {1} + + + + Percentage failed threshold is {0}% and {1}% tests failed ({2}/{3}) + Пороговое значение доли неудачных теÑтов — {0}%, и Ð´Ð¾Ð»Ñ Ð½ÐµÑƒÐ´Ð°Ñ‡Ð½Ñ‹Ñ… теÑтов — {1}% ({2}/{3}) + + + + +===================== +Moving last attempt asset files to the default result directory +===================== + + +===================== +Перемещение файлов реÑурÑов поÑледней попытки в каталог результатов по умолчанию +===================== + + + + + Moving file '{0}' to '{1}' + Перемещение файла "{0}" в "{1}" + + + + Failed to start process '{0}' + Ðе удалоÑÑŒ запуÑтить процеÑÑ "{0}". + + + + Retry failed tests feature allows to restart test execution upon failure. + Ð¤ÑƒÐ½ÐºÑ†Ð¸Ñ Ð¿Ð¾Ð²Ñ‚Ð¾Ñ€Ð° неудачных теÑтов позволÑет перезапуÑтить выполнение теÑта поÑле ÑбоÑ. + + + + Retry failed tests + Включить повтор неудачных теÑтов + + + + Retry failed tests only works with builders of type 'Microsoft.Testing.Platform.Builder.TestApplicationBuilder' + Повтор неудачных теÑтов работает только Ñ Ð¿Ð¾ÑтроителÑми типа "Microsoft.Testing.Platform.Builder.TestApplicationBuilder" + + + + Disable retry mechanism if the percentage of failed tests is greater than the specified value + Отключить механизм повторных попыток, еÑли процент неудачных теÑтов превышает указанное значение + + + + Disable retry mechanism if the number of failed tests is greater than the specified value + Отключить механизм повторных попыток, еÑли чиÑло неудачных теÑтов превышает указанное значение + + + + Retry failed tests feature is not supported in hot reload mode + Ð¤ÑƒÐ½ÐºÑ†Ð¸Ñ Ð¿Ð¾Ð²Ñ‚Ð¾Ñ€Ð° неудачных теÑтов не поддерживаетÑÑ Ð² режиме горÑчей перезагрузки + + + + Retry failed tests feature is not supported in server mode + Ð¤ÑƒÐ½ÐºÑ†Ð¸Ñ Ð¿Ð¾Ð²Ñ‚Ð¾Ñ€Ð° неудачных теÑтов не поддерживаетÑÑ Ð² режиме Ñервера + + + + Enable retry failed tests + Включить повтор неудачных теÑтов + + + + Option '{0}' requires option '{1}' to be specified + Параметр "{0}" требует ÑƒÐºÐ°Ð·Ð°Ð½Ð¸Ñ Ð¿Ð°Ñ€Ð°Ð¼ÐµÑ‚Ñ€Ð° "{1}" + + + + Option '{0}' expects a single integer argument + Параметр "{0}" ожидает один целочиÑленный аргумент + + + + Options '{0}' and '{1}' cannot be used together + Параметры "{0}" и "{1}" не могут иÑпользоватьÑÑ Ð²Ð¼ÐµÑте + + + + Test host process exited before the retry service could connect to it. Exit code: {0} + ТеÑтовый хоÑÑ‚-процеÑÑ Ð·Ð°Ð²ÐµÑ€ÑˆÐ¸Ð»ÑÑ Ð¿Ñ€ÐµÐ¶Ð´Ðµ, чем к нему Ñмогла подключитьÑÑ Ñлужба повторной попытки. Код выхода: {0} + + + + +===================== +Tests suite completed successfully in {0} attempts +===================== + +===================== +Ðабор теÑтов уÑпешно завершен за неÑколько ({0}) попыток +===================== + + + + +===================== +Tests suite failed, total failed tests: {0}, exit code: {1}, attempt: {2}/{3} +===================== + + +===================== +Сбой набора теÑтов. Ð’Ñего неудачных теÑтов: {0}, код завершениÑ: {1}, попытка: {2}/{3} +===================== + + + + + +===================== +Tests suite failed in all {0} attempts +===================== + +===================== +Сбой набора теÑтов во вÑех попытках ({0}) +===================== + + + + Test suite failed with and exit code different that 2 (failed tests). Failure related to an unexpected condition. Exit code '{0}' + Сбой набора теÑтов Ñ ÐºÐ¾Ð´Ð¾Ð¼ завершениÑ, который отличаетÑÑ Ð¾Ñ‚ 2 (неудачные теÑÑ‚Ñ‹). Сбой, ÑвÑзанный Ñ Ð½ÐµÐ¾Ð¶Ð¸Ð´Ð°Ð½Ð½Ñ‹Ð¼ уÑловием. Код Ð·Ð°Ð²ÐµÑ€ÑˆÐµÐ½Ð¸Ñ "{0}" + + + + + \ No newline at end of file diff --git a/src/Platform/Microsoft.Testing.Extensions.Retry/Resources/xlf/ExtensionResources.tr.xlf b/src/Platform/Microsoft.Testing.Extensions.Retry/Resources/xlf/ExtensionResources.tr.xlf new file mode 100644 index 0000000000..74dd39e0dd --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.Retry/Resources/xlf/ExtensionResources.tr.xlf @@ -0,0 +1,150 @@ + + + + + + Failed to create retries directory due to collisions in '{0}' despite re-trying. + Yeniden denemeye raÄŸmen '{0} ' içindeki çakışmalar nedeniyle yeniden deneme dizini oluÅŸturulamadı. + '{0}' is the directory where collisions happen + + + Failure threshold policy is enabled, failed tests will not be restarted. + Hata eÅŸiÄŸi ilkesi etkinleÅŸtirildi, baÅŸarısız testler yeniden baÅŸlatılmaz. + + + + Maximum failed tests threshold is {0} and {1} tests failed + Maksimum baÅŸarısız test eÅŸiÄŸi {0} ve {1} test baÅŸarısız oldu + + + + Percentage failed threshold is {0}% and {1}% tests failed ({2}/{3}) + BaÅŸarısız olan yüzde eÅŸiÄŸi %{0} ve baÅŸarısız %{1} ({2}/{3}) + + + + +===================== +Moving last attempt asset files to the default result directory +===================== + + +===================== +Son deneme varlık dosyaları, varsayılan sonuç dizinine taşınıyor +===================== + + + + + Moving file '{0}' to '{1}' + '{0}' dosyasını '{1}'e taşıma + + + + Failed to start process '{0}' + '{0}' iÅŸlemi baÅŸlatılamadı + + + + Retry failed tests feature allows to restart test execution upon failure. + BaÅŸarısız testleri yeniden dene özelliÄŸi, baÅŸarısızlık durumunda test yürütmenin yeniden baÅŸlatılmasına olanak tanır. + + + + Retry failed tests + BaÅŸarısız testleri yeniden deneyin + + + + Retry failed tests only works with builders of type 'Microsoft.Testing.Platform.Builder.TestApplicationBuilder' + BaÅŸarısız testleri yeniden deneme yalnızca 'Microsoft.Testing.Platform.Builder.TestApplicationBuilder' türündeki oluÅŸturucularla çalışır + + + + Disable retry mechanism if the percentage of failed tests is greater than the specified value + BaÅŸarısız testlerin yüzdesi belirtilen deÄŸerden büyükse yeniden deneme mekanizmasını devre dışı bırak + + + + Disable retry mechanism if the number of failed tests is greater than the specified value + BaÅŸarısız testlerin yüzdesi belirtilen deÄŸerden büyükse yeniden deneme mekanizmasını devre dışı bırak + + + + Retry failed tests feature is not supported in hot reload mode + BaÅŸarısız testleri yeniden deneme özelliÄŸi, çalışırken yeniden yükleme modunda desteklenmez + + + + Retry failed tests feature is not supported in server mode + BaÅŸarısız testleri yeniden deneme özelliÄŸi sunucu modunda desteklenmiyor + + + + Enable retry failed tests + BaÅŸarısız testleri yeniden denemeyi etkinleÅŸtir + + + + Option '{0}' requires option '{1}' to be specified + '{0}' seçeneÄŸi, '{1}' seçeneÄŸinin belirtilmesini gerektirir + + + + Option '{0}' expects a single integer argument + Seçenek '{0}' tek bir tamsayı argümanı bekliyor + + + + Options '{0}' and '{1}' cannot be used together + '{0}' ve '{1}' seçenekleri birlikte kullanılamaz + + + + Test host process exited before the retry service could connect to it. Exit code: {0} + Yeniden deneme hizmeti ona baÄŸlanamadan test ana makinesi iÅŸleminden çıkıldı. Çıkış kodu: {0} + + + + +===================== +Tests suite completed successfully in {0} attempts +===================== + +===================== +Test paketi {0}denemede baÅŸarıyla tamamlandı +===================== + + + + +===================== +Tests suite failed, total failed tests: {0}, exit code: {1}, attempt: {2}/{3} +===================== + + +===================== +Test paketi baÅŸarısız oldu, toplam baÅŸarısız test: {0}, çıkış kodu: {1}, deneme: {2}/{3} +===================== + + + + + +===================== +Tests suite failed in all {0} attempts +===================== + +===================== +Test paketi tüm {0} denemede baÅŸarısız oldu +===================== + + + + Test suite failed with and exit code different that 2 (failed tests). Failure related to an unexpected condition. Exit code '{0}' + Test paketi 2 (baÅŸarısız testler) kodundan farklı bir çıkış koduyla baÅŸarısız oldu. Beklenmeyen bir koÅŸulla ilgili hata. Çıkış kodu '{0}' + + + + + \ No newline at end of file diff --git a/src/Platform/Microsoft.Testing.Extensions.Retry/Resources/xlf/ExtensionResources.zh-Hans.xlf b/src/Platform/Microsoft.Testing.Extensions.Retry/Resources/xlf/ExtensionResources.zh-Hans.xlf new file mode 100644 index 0000000000..9b66baf8c6 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.Retry/Resources/xlf/ExtensionResources.zh-Hans.xlf @@ -0,0 +1,150 @@ + + + + + + Failed to create retries directory due to collisions in '{0}' despite re-trying. + 未能创建é‡è¯•ç›®å½•ï¼Œå› ä¸ºå°½ç®¡é‡è¯•ï¼Œä½†â€œ{0}â€ä¸­å­˜åœ¨å†²çªã€‚ + '{0}' is the directory where collisions happen + + + Failure threshold policy is enabled, failed tests will not be restarted. + å·²å¯ç”¨å¤±è´¥é˜ˆå€¼ç­–略,将ä¸ä¼šé‡æ–°å¯åŠ¨å¤±è´¥çš„测试。 + + + + Maximum failed tests threshold is {0} and {1} tests failed + 最大失败测试阈值为 {0},有 {1} 测试失败 + + + + Percentage failed threshold is {0}% and {1}% tests failed ({2}/{3}) + 失败的阈值百分比为 {0}%,有 {1}% 测试失败({2}/{3}) + + + + +===================== +Moving last attempt asset files to the default result directory +===================== + + +===================== +正在将上次å°è¯•èµ„产文件移动到默认结果目录 +===================== + + + + + Moving file '{0}' to '{1}' + 正在将文件“{0}â€ç§»åŠ¨åˆ°â€œ{1}†+ + + + Failed to start process '{0}' + 无法å¯åŠ¨è¿›ç¨‹â€œ{0}†+ + + + Retry failed tests feature allows to restart test execution upon failure. + é‡è¯•å¤±è´¥çš„测试功能å…许在失败时é‡æ–°å¯åŠ¨æµ‹è¯•æ‰§è¡Œã€‚ + + + + Retry failed tests + é‡è¯•å¤±è´¥çš„测试 + + + + Retry failed tests only works with builders of type 'Microsoft.Testing.Platform.Builder.TestApplicationBuilder' + é‡è¯•å¤±è´¥çš„测试仅适用于“Microsoft.Testing.Platform.Builder.TestApplicationBuilderâ€ç±»åž‹çš„生æˆå™¨ + + + + Disable retry mechanism if the percentage of failed tests is greater than the specified value + 如果失败的测试百分比大于指定值,则ç¦ç”¨é‡è¯•æœºåˆ¶ + + + + Disable retry mechanism if the number of failed tests is greater than the specified value + 如果失败的测试数大于指定值,则ç¦ç”¨é‡è¯•æœºåˆ¶ + + + + Retry failed tests feature is not supported in hot reload mode + 热é‡è½½æ¨¡å¼ä¸‹ä¸æ”¯æŒé‡è¯•å¤±è´¥çš„测试功能 + + + + Retry failed tests feature is not supported in server mode + æœåŠ¡å™¨æ¨¡å¼ä¸æ”¯æŒé‡è¯•å¤±è´¥çš„测试功能 + + + + Enable retry failed tests + å¯ç”¨é‡è¯•å¤±è´¥çš„测试 + + + + Option '{0}' requires option '{1}' to be specified + 选项“{0}â€éœ€è¦æŒ‡å®šé€‰é¡¹â€œ{1}†+ + + + Option '{0}' expects a single integer argument + 选项“{0}â€éœ€è¦å•ä¸€æ•´æ•°å‚æ•° + + + + Options '{0}' and '{1}' cannot be used together + 选项“{0}â€å’Œâ€œ{1}â€ä¸èƒ½ä¸€èµ·ä½¿ç”¨ + + + + Test host process exited before the retry service could connect to it. Exit code: {0} + 测试主机进程已在é‡è¯•æœåŠ¡å¯ä»¥è¿žæŽ¥åˆ°å®ƒä¹‹å‰é€€å‡ºã€‚退出代ç : {0} + + + + +===================== +Tests suite completed successfully in {0} attempts +===================== + +===================== +测试套件在“{0}â€æ¬¡å°è¯•ä¸­æˆåŠŸå®Œæˆ +===================== + + + + +===================== +Tests suite failed, total failed tests: {0}, exit code: {1}, attempt: {2}/{3} +===================== + + +===================== +测试套件失败,失败的测试总数: {0},退出代ç : {1},å°è¯•æ¬¡æ•°: {2}/{3} +===================== + + + + + +===================== +Tests suite failed in all {0} attempts +===================== + +===================== +测试套件在所有“{0}â€å°è¯•ä¸­å‡å¤±è´¥ +===================== + + + + Test suite failed with and exit code different that 2 (failed tests). Failure related to an unexpected condition. Exit code '{0}' + 测试套件失败,且退出代ç ä¸æ˜¯ 2 (测试失败)。与æ„外情况相关的失败。退出代ç â€œ{0}†+ + + + + \ No newline at end of file diff --git a/src/Platform/Microsoft.Testing.Extensions.Retry/Resources/xlf/ExtensionResources.zh-Hant.xlf b/src/Platform/Microsoft.Testing.Extensions.Retry/Resources/xlf/ExtensionResources.zh-Hant.xlf new file mode 100644 index 0000000000..3113a1b002 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.Retry/Resources/xlf/ExtensionResources.zh-Hant.xlf @@ -0,0 +1,150 @@ + + + + + + Failed to create retries directory due to collisions in '{0}' despite re-trying. + 無法建立é‡è©¦ç›®éŒ„,因為 '{0}' 中發生è¡çªï¼Œä½†ä»åœ¨é‡è©¦ã€‚ + '{0}' is the directory where collisions happen + + + Failure threshold policy is enabled, failed tests will not be restarted. + 已啟用失敗節æµåŽŸå‰‡ï¼Œå°‡ä¸æœƒé‡æ–°å•Ÿå‹•å¤±æ•—的測試。 + + + + Maximum failed tests threshold is {0} and {1} tests failed + 失敗的測試閾值上é™æ˜¯ {0},有 {1} 個測試失敗 + + + + Percentage failed threshold is {0}% and {1}% tests failed ({2}/{3}) + 失敗的百分比閾值為 {0}%,有 {1}% 個測試失敗 ({2}/{3}) + + + + +===================== +Moving last attempt asset files to the default result directory +===================== + + +===================== +將上次嘗試的資產檔案移至é è¨­çµæžœç›®éŒ„ +===================== + + + + + Moving file '{0}' to '{1}' + 正在將檔案 '{0}' 移至 '{1}' + + + + Failed to start process '{0}' + 無法啟動處ç†ç¨‹åº '{0}' + + + + Retry failed tests feature allows to restart test execution upon failure. + é‡è©¦å¤±æ•—的測試功能å…許在失敗時é‡æ–°å•Ÿå‹•æ¸¬è©¦åŸ·è¡Œã€‚ + + + + Retry failed tests + é‡è©¦å¤±æ•—的測試 + + + + Retry failed tests only works with builders of type 'Microsoft.Testing.Platform.Builder.TestApplicationBuilder' + é‡è©¦å¤±æ•—的測試僅é©ç”¨æ–¼é¡žåž‹ç‚º 'Microsoft.Testing.Platform.Builder.TestApplicationBuilder' 的建立器 + + + + Disable retry mechanism if the percentage of failed tests is greater than the specified value + 如果失敗的測試百分比大於指定的值,則åœç”¨é‡è©¦æ©Ÿåˆ¶ + + + + Disable retry mechanism if the number of failed tests is greater than the specified value + 如果失敗的測試數目大於指定的值,則åœç”¨é‡è©¦æ©Ÿåˆ¶ + + + + Retry failed tests feature is not supported in hot reload mode + 熱é‡æ–°è¼‰å…¥æ¨¡å¼ä¸æ”¯æ´é‡è©¦å¤±æ•—的測試功能 + + + + Retry failed tests feature is not supported in server mode + 伺æœå™¨æ¨¡å¼ä¸æ”¯æ´é‡è©¦å¤±æ•—的測試功能 + + + + Enable retry failed tests + 啟用é‡è©¦å¤±æ•—的測試 + + + + Option '{0}' requires option '{1}' to be specified + 使用 '{0}' é¸é …時必須指定é¸é … '{1}'。 + + + + Option '{0}' expects a single integer argument + é¸é … '{0}' 需è¦å–®ä¸€æ•´æ•¸å¼•æ•¸ + + + + Options '{0}' and '{1}' cannot be used together + é¸é … '{0}' å’Œ '{1}' ä¸èƒ½åŒæ™‚使用 + + + + Test host process exited before the retry service could connect to it. Exit code: {0} + 測試主機處ç†åºåœ¨é‡è©¦æœå‹™é€£ç·šåˆ°å®ƒä¹‹å‰å·²çµæŸã€‚çµæŸä»£ç¢¼: {0} + + + + +===================== +Tests suite completed successfully in {0} attempts +===================== + +===================== +測試套件已順利在 {0} æ¬¡å˜—è©¦ä¸­å®Œæˆ +===================== + + + + +===================== +Tests suite failed, total failed tests: {0}, exit code: {1}, attempt: {2}/{3} +===================== + + +===================== +測試套件失敗,失敗的測試總數: {0},çµæŸä»£ç¢¼: {1},嘗試: {2}/{3} +===================== + + + + + +===================== +Tests suite failed in all {0} attempts +===================== + +===================== +測試套件在所有 {0} 次嘗試中都失敗 +===================== + + + + Test suite failed with and exit code different that 2 (failed tests). Failure related to an unexpected condition. Exit code '{0}' + 測試套件失敗,çµæŸä»£ç¢¼èˆ‡ 2 ä¸åŒ (測試失敗)。與未é æœŸæƒ…况有關的失敗。çµæŸä»£ç¢¼ '{0}' + + + + + \ No newline at end of file diff --git a/src/Platform/Microsoft.Testing.Extensions.Retry/RetryCommandLineOptionsProvider.cs b/src/Platform/Microsoft.Testing.Extensions.Retry/RetryCommandLineOptionsProvider.cs new file mode 100644 index 0000000000..40ee1ac50d --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.Retry/RetryCommandLineOptionsProvider.cs @@ -0,0 +1,85 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under dual-license. See LICENSE.PLATFORMTOOLS.txt file in the project root for full license information. + +using System.Globalization; + +using Microsoft.Testing.Extensions.Policy.Resources; +using Microsoft.Testing.Platform.CommandLine; +using Microsoft.Testing.Platform.Extensions; +using Microsoft.Testing.Platform.Extensions.CommandLine; +using Microsoft.Testing.Platform.Helpers; + +namespace Microsoft.Testing.Extensions.Policy; + +internal sealed class RetryCommandLineOptionsProvider : ICommandLineOptionsProvider +{ + public const string RetryFailedTestsOptionName = "retry-failed-tests"; + public const string RetryFailedTestsMaxPercentageOptionName = "retry-failed-tests-max-percentage"; + public const string RetryFailedTestsMaxTestsOptionName = "retry-failed-tests-max-tests"; + public const string RetryFailedTestsPipeNameOptionName = "internal-retry-pipename"; + + public string Uid => nameof(RetryCommandLineOptionsProvider); + + public string Version => AppVersion.DefaultSemVer; + + public string DisplayName => ExtensionResources.RetryFailedTestsExtensionDisplayName; + + public string Description => ExtensionResources.RetryFailedTestsExtensionDescription; + + public IReadOnlyCollection GetCommandLineOptions() + => new CommandLineOption[] + { + // 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), + new(RetryFailedTestsMaxPercentageOptionName, ExtensionResources.RetryFailedTestsMaxPercentageOptionDescription, ArgumentArity.ExactlyOne, false, isBuiltIn: true), + 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), + }; + + public Task IsEnabledAsync() => Task.FromResult(true); + + public Task ValidateCommandLineOptionsAsync(ICommandLineOptions commandLineOptions) + { + if (commandLineOptions.IsOptionSet(RetryFailedTestsMaxPercentageOptionName) && commandLineOptions.IsOptionSet(RetryFailedTestsMaxTestsOptionName)) + { + return ValidationResult.InvalidTask(string.Format(CultureInfo.CurrentCulture, ExtensionResources.RetryFailedTestsPercentageAndCountCannotBeMixedErrorMessage, RetryFailedTestsMaxPercentageOptionName, RetryFailedTestsMaxTestsOptionName)); + } + + if (commandLineOptions.IsOptionSet(RetryFailedTestsMaxPercentageOptionName) && !commandLineOptions.IsOptionSet(RetryFailedTestsOptionName)) + { + return ValidationResult.InvalidTask(string.Format(CultureInfo.CurrentCulture, ExtensionResources.RetryFailedTestsOptionIsMissingErrorMessage, RetryFailedTestsMaxPercentageOptionName, RetryFailedTestsOptionName)); + } + + if (commandLineOptions.IsOptionSet(RetryFailedTestsMaxTestsOptionName) && !commandLineOptions.IsOptionSet(RetryFailedTestsOptionName)) + { + return ValidationResult.InvalidTask(string.Format(CultureInfo.CurrentCulture, ExtensionResources.RetryFailedTestsOptionIsMissingErrorMessage, RetryFailedTestsMaxTestsOptionName, RetryFailedTestsOptionName)); + } + + // No problem found + return ValidationResult.ValidTask; + } + + public Task ValidateOptionArgumentsAsync(CommandLineOption commandOption, string[] arguments) + { + if (commandOption.Name == RetryFailedTestsOptionName && !int.TryParse(arguments[0], out int _)) + { + return ValidationResult.InvalidTask(string.Format(CultureInfo.CurrentCulture, ExtensionResources.RetryFailedTestsOptionSingleIntegerArgumentErrorMessage, RetryFailedTestsOptionName)); + } + + if (commandOption.Name == RetryFailedTestsMaxPercentageOptionName && !int.TryParse(arguments[0], out int _)) + { + return ValidationResult.InvalidTask(string.Format(CultureInfo.CurrentCulture, ExtensionResources.RetryFailedTestsOptionSingleIntegerArgumentErrorMessage, RetryFailedTestsMaxPercentageOptionName)); + } + + if (commandOption.Name == RetryFailedTestsMaxTestsOptionName && !int.TryParse(arguments[0], out int _)) + { + return ValidationResult.InvalidTask(string.Format(CultureInfo.CurrentCulture, ExtensionResources.RetryFailedTestsOptionSingleIntegerArgumentErrorMessage, RetryFailedTestsMaxTestsOptionName)); + } + + // No problem found + return ValidationResult.ValidTask; + } +} diff --git a/src/Platform/Microsoft.Testing.Extensions.Retry/RetryDataConsumer.cs b/src/Platform/Microsoft.Testing.Extensions.Retry/RetryDataConsumer.cs new file mode 100644 index 0000000000..644e934405 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.Retry/RetryDataConsumer.cs @@ -0,0 +1,79 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under dual-license. See LICENSE.PLATFORMTOOLS.txt file in the project root for full license information. + +using Microsoft.Testing.Extensions.Policy.Resources; +using Microsoft.Testing.Platform.CommandLine; +using Microsoft.Testing.Platform.Extensions; +using Microsoft.Testing.Platform.Extensions.Messages; +using Microsoft.Testing.Platform.Extensions.RetryFailedTests.Serializers; +using Microsoft.Testing.Platform.Extensions.TestHost; +using Microsoft.Testing.Platform.Helpers; +using Microsoft.Testing.Platform.IPC.Models; +using Microsoft.Testing.Platform.Messages; +using Microsoft.Testing.Platform.Services; +using Microsoft.Testing.Platform.TestHost; + +namespace Microsoft.Testing.Extensions.Policy; + +internal sealed class RetryDataConsumer : IDataConsumer, ITestSessionLifetimeHandler, IAsyncInitializableExtension +{ + private readonly IServiceProvider _serviceProvider; + private readonly ICommandLineOptions _commandLineOptions; + private RetryLifecycleCallbacks? _retryFailedTestsLifecycleCallbacks; + private int _totalTests; + + public RetryDataConsumer(IServiceProvider serviceProvider) + { + _serviceProvider = serviceProvider; + _commandLineOptions = _serviceProvider.GetCommandLineOptions(); + } + + public Type[] DataTypesConsumed => new[] { typeof(TestNodeUpdateMessage) }; + + public string Uid => nameof(RetryDataConsumer); + + public string Version => AppVersion.DefaultSemVer; + + public string DisplayName => ExtensionResources.RetryFailedTestsExtensionDisplayName; + + public string Description => ExtensionResources.RetryFailedTestsExtensionDescription; + + public async Task ConsumeAsync(IDataProducer dataProducer, IData value, CancellationToken cancellationToken) + { + var testNodeUpdateMessage = (TestNodeUpdateMessage)value; + TestNodeStateProperty nodeState = testNodeUpdateMessage.TestNode.Properties.Single(); + if (Array.IndexOf(TestNodePropertiesCategories.WellKnownTestNodeTestRunOutcomeFailedProperties, nodeState.GetType()) != -1) + { + ApplicationStateGuard.Ensure(_retryFailedTestsLifecycleCallbacks is not null); + ApplicationStateGuard.Ensure(_retryFailedTestsLifecycleCallbacks.Client is not null); + await _retryFailedTestsLifecycleCallbacks.Client.RequestReplyAsync(new FailedTestRequest(testNodeUpdateMessage.TestNode.Uid), cancellationToken); + } + + if (Array.IndexOf(TestNodePropertiesCategories.WellKnownTestNodeTestRunOutcomeProperties, nodeState.GetType()) != -1) + { + _totalTests++; + } + } + + public async Task OnTestSessionFinishingAsync(SessionUid sessionUid, CancellationToken cancellationToken) + { + ApplicationStateGuard.Ensure(_retryFailedTestsLifecycleCallbacks is not null); + ApplicationStateGuard.Ensure(_retryFailedTestsLifecycleCallbacks.Client is not null); + await _retryFailedTestsLifecycleCallbacks.Client.RequestReplyAsync(new TotalTestsRunRequest(_totalTests), cancellationToken); + } + + public Task OnTestSessionStartingAsync(SessionUid sessionUid, CancellationToken cancellationToken) + => Task.CompletedTask; + + public Task IsEnabledAsync() + + => Task.FromResult(_commandLineOptions.IsOptionSet(RetryCommandLineOptionsProvider.RetryFailedTestsPipeNameOptionName)); + + public async Task InitializeAsync() + { + if (await IsEnabledAsync()) + { + _retryFailedTestsLifecycleCallbacks = _serviceProvider.GetRequiredService(); + } + } +} diff --git a/src/Platform/Microsoft.Testing.Extensions.Retry/RetryExecutionFilterFactory.cs b/src/Platform/Microsoft.Testing.Extensions.Retry/RetryExecutionFilterFactory.cs new file mode 100644 index 0000000000..09bedd3afa --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.Retry/RetryExecutionFilterFactory.cs @@ -0,0 +1,50 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under dual-license. See LICENSE.PLATFORMTOOLS.txt file in the project root for full license information. + +using Microsoft.Testing.Extensions.Policy.Resources; +using Microsoft.Testing.Platform.CommandLine; +using Microsoft.Testing.Platform.Extensions.Messages; +using Microsoft.Testing.Platform.Helpers; +using Microsoft.Testing.Platform.Requests; +using Microsoft.Testing.Platform.Services; + +namespace Microsoft.Testing.Extensions.Policy; + +internal sealed class RetryExecutionFilterFactory : ITestExecutionFilterFactory +{ + private readonly IServiceProvider _serviceProvider; + private readonly ICommandLineOptions _commandLineOptions; + private RetryLifecycleCallbacks? _retryFailedTestsLifecycleCallbacks; + + public RetryExecutionFilterFactory(IServiceProvider serviceProvider) + { + _serviceProvider = serviceProvider; + _commandLineOptions = serviceProvider.GetCommandLineOptions(); + } + + public string Uid => nameof(RetryExecutionFilterFactory); + + public string Version => AppVersion.DefaultSemVer; + + public string DisplayName => ExtensionResources.RetryFailedTestsExtensionDisplayName; + + public string Description => ExtensionResources.RetryFailedTestsExtensionDescription; + + public Task IsEnabledAsync() + => Task.FromResult(_commandLineOptions.IsOptionSet(RetryCommandLineOptionsProvider.RetryFailedTestsPipeNameOptionName)); + + public async Task<(bool, ITestExecutionFilter?)> TryCreateAsync() + { + _retryFailedTestsLifecycleCallbacks = _serviceProvider.GetRequiredService(); + if (_retryFailedTestsLifecycleCallbacks.FailedTestsIDToRetry?.Length > 0) + { + return (true, (ITestExecutionFilter?)new TestNodeUidListFilter(_retryFailedTestsLifecycleCallbacks.FailedTestsIDToRetry + .Select(x => new TestNodeUid(x)).ToArray())); + } + else + { + ConsoleTestExecutionFilterFactory consoleTestExecutionFilterFactory = new(_commandLineOptions); + return await consoleTestExecutionFilterFactory.TryCreateAsync(); + } + } +} diff --git a/src/Platform/Microsoft.Testing.Extensions.Retry/RetryExtensions.cs b/src/Platform/Microsoft.Testing.Extensions.Retry/RetryExtensions.cs new file mode 100644 index 0000000000..371c3e4606 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.Retry/RetryExtensions.cs @@ -0,0 +1,38 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under dual-license. See LICENSE.PLATFORMTOOLS.txt file in the project root for full license information. + +using Microsoft.Testing.Extensions.Policy; +using Microsoft.Testing.Extensions.Policy.Resources; +using Microsoft.Testing.Platform.Builder; +using Microsoft.Testing.Platform.Extensions; +using Microsoft.Testing.Platform.Extensions.TestHostOrchestrator; +using Microsoft.Testing.Platform.TestHost; + +namespace Microsoft.Testing.Extensions; + +public static class RetryExtensions +{ + public static void AddRetryProvider(this ITestApplicationBuilder builder) + { + builder.CommandLine.AddProvider(() => new RetryCommandLineOptionsProvider()); + + builder.TestHost.AddTestApplicationLifecycleCallbacks(serviceProvider + => new RetryLifecycleCallbacks(serviceProvider)); + + CompositeExtensionFactory compositeExtensionFactory + = new(serviceProvider => new RetryDataConsumer(serviceProvider)); + builder.TestHost.AddDataConsumer(compositeExtensionFactory); + builder.TestHost.AddTestSessionLifetimeHandle(compositeExtensionFactory); + + if (builder is not TestApplicationBuilder testApplicationBuilder) + { + throw new InvalidOperationException(ExtensionResources.RetryFailedTestsInvalidTestApplicationBuilderErrorMessage); + } + + // Net yet exposed extension points + ((TestHostOrchestratorManager)testApplicationBuilder.TestHostControllersManager) + .AddTestHostOrchestrator(serviceProvider => new RetryOrchestrator(serviceProvider)); + ((TestHostManager)builder.TestHost) + .AddTestExecutionFilterFactory(serviceProvider => new RetryExecutionFilterFactory(serviceProvider)); + } +} diff --git a/src/Platform/Microsoft.Testing.Extensions.Retry/RetryFailedTestsPipeServer.cs b/src/Platform/Microsoft.Testing.Extensions.Retry/RetryFailedTestsPipeServer.cs new file mode 100644 index 0000000000..21032e0497 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.Retry/RetryFailedTestsPipeServer.cs @@ -0,0 +1,75 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under dual-license. See LICENSE.PLATFORMTOOLS.txt file in the project root for full license information. + +using Microsoft.Testing.Platform.Extensions.RetryFailedTests.Serializers; +using Microsoft.Testing.Platform.Helpers; +using Microsoft.Testing.Platform.IPC; +using Microsoft.Testing.Platform.IPC.Models; +using Microsoft.Testing.Platform.IPC.Serializers; +using Microsoft.Testing.Platform.Logging; +using Microsoft.Testing.Platform.Services; + +namespace Microsoft.Testing.Extensions.Policy; + +internal sealed class RetryFailedTestsPipeServer : IDisposable +{ + private readonly NamedPipeServer _singleConnectionNamedPipeServer; + private readonly PipeNameDescription _pipeNameDescription; + private readonly string[] _failedTests; + + public RetryFailedTestsPipeServer(IServiceProvider serviceProvider, string[] failedTests, ILogger logger) + { + _pipeNameDescription = NamedPipeServer.GetPipeName(Guid.NewGuid().ToString("N")); + logger.LogTrace($"Retry server pipe name: '{_pipeNameDescription.Name}'"); + _singleConnectionNamedPipeServer = new NamedPipeServer(_pipeNameDescription, CallbackAsync, + serviceProvider.GetEnvironment(), + serviceProvider.GetLoggerFactory().CreateLogger(), + serviceProvider.GetTask(), + serviceProvider.GetTestApplicationCancellationTokenSource().CancellationToken); + + _singleConnectionNamedPipeServer.RegisterSerializer(new VoidResponseSerializer(), typeof(VoidResponse)); + _singleConnectionNamedPipeServer.RegisterSerializer(new FailedTestRequestSerializer(), typeof(FailedTestRequest)); + _singleConnectionNamedPipeServer.RegisterSerializer(new GetListOfFailedTestsRequestSerializer(), typeof(GetListOfFailedTestsRequest)); + _singleConnectionNamedPipeServer.RegisterSerializer(new GetListOfFailedTestsResponseSerializer(), typeof(GetListOfFailedTestsResponse)); + _singleConnectionNamedPipeServer.RegisterSerializer(new TotalTestsRunRequestSerializer(), typeof(TotalTestsRunRequest)); + _failedTests = failedTests; + } + + public string PipeName => _pipeNameDescription.Name; + + public List? FailedUID { get; private set; } + + public int TotalTestRan { get; private set; } + + public Task WaitForConnectionAsync(CancellationToken cancellationToken) + => _singleConnectionNamedPipeServer.WaitConnectionAsync(cancellationToken); + + public void Dispose() + { + _singleConnectionNamedPipeServer.Dispose(); + _pipeNameDescription.Dispose(); + } + + private Task CallbackAsync(IRequest request) + { + if (request is FailedTestRequest failed) + { + FailedUID ??= new(); + FailedUID.Add(failed.Uid); + return Task.FromResult((IResponse)VoidResponse.CachedInstance); + } + + if (request is GetListOfFailedTestsRequest) + { + return Task.FromResult((IResponse)new GetListOfFailedTestsResponse(_failedTests)); + } + + if (request is TotalTestsRunRequest totalTestsRunRequest) + { + TotalTestRan = totalTestsRunRequest.TotalTests; + return Task.FromResult((IResponse)VoidResponse.CachedInstance); + } + + throw ApplicationStateGuard.Unreachable(); + } +} diff --git a/src/Platform/Microsoft.Testing.Extensions.Retry/RetryLifecycleCallbacks.cs b/src/Platform/Microsoft.Testing.Extensions.Retry/RetryLifecycleCallbacks.cs new file mode 100644 index 0000000000..6d019d9fd1 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.Retry/RetryLifecycleCallbacks.cs @@ -0,0 +1,89 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under dual-license. See LICENSE.PLATFORMTOOLS.txt file in the project root for full license information. + +using Microsoft.Testing.Extensions.Policy.Resources; +using Microsoft.Testing.Platform.CommandLine; +using Microsoft.Testing.Platform.Extensions.RetryFailedTests.Serializers; +using Microsoft.Testing.Platform.Extensions.TestHost; +using Microsoft.Testing.Platform.Helpers; +using Microsoft.Testing.Platform.IPC; +using Microsoft.Testing.Platform.IPC.Models; +using Microsoft.Testing.Platform.IPC.Serializers; +using Microsoft.Testing.Platform.Logging; +using Microsoft.Testing.Platform.Services; + +using Polyfills; + +namespace Microsoft.Testing.Extensions.Policy; + +internal sealed class RetryLifecycleCallbacks : ITestApplicationLifecycleCallbacks, +#if NETCOREAPP + IAsyncDisposable +#else + IDisposable +#endif +{ + private readonly IServiceProvider _serviceProvider; + private readonly ICommandLineOptions _commandLineOptions; + + public RetryLifecycleCallbacks(IServiceProvider serviceProvider) + { + _serviceProvider = serviceProvider; + _commandLineOptions = _serviceProvider.GetCommandLineOptions(); + } + + public NamedPipeClient? Client { get; private set; } + + public string[]? FailedTestsIDToRetry { get; private set; } + + public string Uid => nameof(RetryLifecycleCallbacks); + + public string Version => AppVersion.DefaultSemVer; + + public string DisplayName => ExtensionResources.RetryFailedTestsExtensionDisplayName; + + public string Description => ExtensionResources.RetryFailedTestsExtensionDescription; + + public async Task BeforeRunAsync(CancellationToken cancellationToken) + { + if (!_commandLineOptions.TryGetOptionArgumentList(RetryCommandLineOptionsProvider.RetryFailedTestsPipeNameOptionName, out string[]? pipeName)) + { + throw ApplicationStateGuard.Unreachable(); + } + + ILogger logger = _serviceProvider.GetLoggerFactory().CreateLogger(); + + Guard.NotNull(pipeName); + ArgumentGuard.Ensure(pipeName.Length == 1, nameof(pipeName), "Pipe name expected"); + logger.LogDebug($"Connecting to pipe '{pipeName[0]}'"); + + Client = new(pipeName[0]); + Client.RegisterSerializer(new VoidResponseSerializer(), typeof(VoidResponse)); + Client.RegisterSerializer(new FailedTestRequestSerializer(), typeof(FailedTestRequest)); + Client.RegisterSerializer(new GetListOfFailedTestsRequestSerializer(), typeof(GetListOfFailedTestsRequest)); + Client.RegisterSerializer(new GetListOfFailedTestsResponseSerializer(), typeof(GetListOfFailedTestsResponse)); + Client.RegisterSerializer(new TotalTestsRunRequestSerializer(), typeof(TotalTestsRunRequest)); + await Client.ConnectAsync(cancellationToken); + + GetListOfFailedTestsResponse result = await Client.RequestReplyAsync(new GetListOfFailedTestsRequest(), cancellationToken); + FailedTestsIDToRetry = result.FailedTestIds; + } + + public Task IsEnabledAsync() + => Task.FromResult(_commandLineOptions.IsOptionSet(RetryCommandLineOptionsProvider.RetryFailedTestsPipeNameOptionName)); + + public Task AfterRunAsync(int exitCode, CancellationToken cancellation) + => Task.CompletedTask; + +#if NETCOREAPP + public async ValueTask DisposeAsync() + { + if (Client is not null) + { + await Client.DisposeAsync(); + } + } +#else + public void Dispose() => Client?.Dispose(); +#endif +} diff --git a/src/Platform/Microsoft.Testing.Extensions.Retry/RetryOrchestrator.cs b/src/Platform/Microsoft.Testing.Extensions.Retry/RetryOrchestrator.cs new file mode 100644 index 0000000000..849d286da2 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.Retry/RetryOrchestrator.cs @@ -0,0 +1,348 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under dual-license. See LICENSE.PLATFORMTOOLS.txt file in the project root for full license information. + +using System.Diagnostics; +using System.Globalization; +using System.Text; + +using Microsoft.Testing.Extensions.Policy.Resources; +using Microsoft.Testing.Platform.CommandLine; +using Microsoft.Testing.Platform.Configurations; +using Microsoft.Testing.Platform.Extensions.OutputDevice; +using Microsoft.Testing.Platform.Extensions.TestHostOrchestrator; +using Microsoft.Testing.Platform.Helpers; +using Microsoft.Testing.Platform.Logging; +using Microsoft.Testing.Platform.OutputDevice; +using Microsoft.Testing.Platform.Services; + +namespace Microsoft.Testing.Extensions.Policy; + +internal sealed class RetryOrchestrator : ITestHostOrchestrator, IOutputDeviceDataProducer +{ + private readonly IServiceProvider _serviceProvider; + private readonly ICommandLineOptions _commandLineOptions; + + public RetryOrchestrator(IServiceProvider serviceProvider) + { + _serviceProvider = serviceProvider; + _commandLineOptions = _serviceProvider.GetCommandLineOptions(); + } + + public string Uid => nameof(RetryOrchestrator); + + public string Version => AppVersion.DefaultSemVer; + + public string DisplayName => ExtensionResources.RetryFailedTestsExtensionDisplayName; + + public string Description => ExtensionResources.RetryFailedTestsExtensionDescription; + + public Task IsEnabledAsync() + => Task.FromResult(_commandLineOptions.IsOptionSet(RetryCommandLineOptionsProvider.RetryFailedTestsOptionName)); + + private static 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)) + { + continue; + } + + try + { + Directory.CreateDirectory(retryRootFolder); + return retryRootFolder; + } + catch (IOException ex) + { + lastException = ex; + } + } + + if (lastException is not null) + { + throw lastException; + } + + throw new IOException(string.Format(CultureInfo.InvariantCulture, ExtensionResources.FailedToCreateRetryDirectoryBecauseOfCollision, resultDirectory)); + } + + public async Task OrchestrateTestHostExecutionAsync() + { + if (_commandLineOptions.IsOptionSet(PlatformCommandLineProvider.ServerOptionKey)) + { + throw new InvalidOperationException(ExtensionResources.RetryFailedTestsNotSupportedInServerModeErrorMessage); + } + + if (IsHotReloadEnabled(_serviceProvider.GetEnvironment())) + { + throw new InvalidOperationException(ExtensionResources.RetryFailedTestsNotSupportedInHotReloadErrorMessage); + } + + ILogger logger = _serviceProvider.GetLoggerFactory().CreateLogger(); + IConfiguration configuration = _serviceProvider.GetConfiguration(); + + ITestApplicationModuleInfo currentTestApplicationModuleInfo = _serviceProvider.GetTestApplicationModuleInfo(); + ExecutableInfo executableInfo = currentTestApplicationModuleInfo.GetCurrentExecutableInfo(); + + if (!_commandLineOptions.TryGetOptionArgumentList(RetryCommandLineOptionsProvider.RetryFailedTestsOptionName, out string[]? cmdRetries)) + { + throw ApplicationStateGuard.Unreachable(); + } + + ApplicationStateGuard.Ensure(cmdRetries is not null); + 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(); + int argIndex = GetOptionArgumentIndex(RetryCommandLineOptionsProvider.RetryFailedTestsOptionName, executableArguments); + if (argIndex < 0) + { + throw ApplicationStateGuard.Unreachable(); + } + + indexToCleanup.Add(argIndex); + indexToCleanup.Add(argIndex + 1); + + argIndex = GetOptionArgumentIndex(RetryCommandLineOptionsProvider.RetryFailedTestsMaxPercentageOptionName, executableArguments); + if (argIndex > -1) + { + indexToCleanup.Add(argIndex); + indexToCleanup.Add(argIndex + 1); + } + + argIndex = GetOptionArgumentIndex(RetryCommandLineOptionsProvider.RetryFailedTestsMaxTestsOptionName, executableArguments); + if (argIndex > -1) + { + indexToCleanup.Add(argIndex); + indexToCleanup.Add(argIndex + 1); + } + + argIndex = GetOptionArgumentIndex(PlatformCommandLineProvider.ResultDirectoryOptionKey, executableArguments); + if (argIndex > -1) + { + indexToCleanup.Add(argIndex); + indexToCleanup.Add(argIndex + 1); + } + + // Override the result directory with the attempt one + string resultDirectory = configuration.GetTestResultDirectory(); + + List exitCodes = new(); + IOutputDevice outputDevice = _serviceProvider.GetOutputDevice(); + IFileSystem fileSystem = _serviceProvider.GetFileSystem(); + + int attemptCount = 0; + List finalArguments = new(); + string[]? lastListOfFailedId = null; + string? currentTryResultFolder = null; + bool thresholdPolicyKickedIn = false; + string retryRootFolder = CreateRetriesDirectory(resultDirectory); + bool retryInterrupted = false; + while (attemptCount < userMaxRetryCount + 1) + { + attemptCount++; + + // Cleanup the arguments + for (int i = 0; i < executableArguments.Length; i++) + { + if (indexToCleanup.Contains(i)) + { + continue; + } + + finalArguments.Add(executableArguments[i]); + } + + // Fix result folder + currentTryResultFolder = Path.Combine(retryRootFolder, attemptCount.ToString(CultureInfo.InvariantCulture)); + finalArguments.Add($"--{PlatformCommandLineProvider.ResultDirectoryOptionKey}"); + finalArguments.Add(currentTryResultFolder); + + // Prepare the pipeserver + using RetryFailedTestsPipeServer retryFailedTestsPipeServer = new(_serviceProvider, lastListOfFailedId ?? Array.Empty(), logger); + finalArguments.Add($"--{RetryCommandLineOptionsProvider.RetryFailedTestsPipeNameOptionName}"); + finalArguments.Add(retryFailedTestsPipeServer.PipeName); + + // Prepare the process start + ProcessStartInfo processStartInfo = new() + { + FileName = executableInfo.FileName, +#if !NETCOREAPP + UseShellExecute = false, +#endif + }; + + foreach (string argument in finalArguments) + { +#if !NETCOREAPP + processStartInfo.Arguments += argument + " "; +#else + processStartInfo.ArgumentList.Add(argument); +#endif + } + + await logger.LogDebugAsync($"Starting test host process, attempt {attemptCount}/{userMaxRetryCount}"); + IProcess testHostProcess = _serviceProvider.GetProcessHandler().Start(processStartInfo) + ?? throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, ExtensionResources.RetryFailedTestsCannotStartProcessErrorMessage, processStartInfo.FileName)); + + CancellationTokenSource processExitedCancellationToken = new(); + testHostProcess.Exited += (sender, e) => + { + processExitedCancellationToken.Cancel(); + var processExited = sender as Process; + logger.LogDebug($"Test host process exited, PID: '{processExited?.Id}'"); + }; + + using (var timeout = new CancellationTokenSource(TimeoutHelper.DefaultHangTimeSpanTimeout)) + 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"); + try + { +#if NETCOREAPP + await retryFailedTestsPipeServer.WaitForConnectionAsync(linkedToken2.Token); +#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); +#endif + } + catch (OperationCanceledException) when (processExitedCancellationToken.IsCancellationRequested) + { + await outputDevice.DisplayAsync(this, new ErrorMessageOutputDeviceData(string.Format(CultureInfo.InvariantCulture, ExtensionResources.TestHostProcessExitedBeforeRetryCouldConnect, testHostProcess.ExitCode))); + return ExitCodes.GenericFailure; + } + } + + await testHostProcess.WaitForExitAsync(); + + 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))); + retryInterrupted = true; + break; + } + + await outputDevice.DisplayAsync(this, new WarningMessageOutputDeviceData(string.Format(CultureInfo.InvariantCulture, ExtensionResources.TestSuiteFailed, retryFailedTestsPipeServer.FailedUID?.Count ?? 0, testHostProcess.ExitCode, attemptCount, userMaxRetryCount + 1))); + + // Check thresholds + if (attemptCount == 1) + { + double? maxFailedTests = null; + double? maxPercentage = null; + double? maxCount = null; + if (_commandLineOptions.TryGetOptionArgumentList(RetryCommandLineOptionsProvider.RetryFailedTestsMaxPercentageOptionName, out string[]? retryFailedTestsMaxPercentage)) + { + maxPercentage = double.Parse(retryFailedTestsMaxPercentage[0], CultureInfo.InvariantCulture); + maxFailedTests = maxPercentage / 100 * retryFailedTestsPipeServer.TotalTestRan; + } + + if (_commandLineOptions.TryGetOptionArgumentList(RetryCommandLineOptionsProvider.RetryFailedTestsMaxTestsOptionName, out string[]? retryFailedTestsMaxCount)) + { + maxCount = double.Parse(retryFailedTestsMaxCount[0], CultureInfo.InvariantCulture); + maxFailedTests = maxCount.Value; + } + + // If threshold policy enable + if (maxFailedTests is not null) + { + if ((retryFailedTestsPipeServer.FailedUID?.Count ?? 0) > maxFailedTests) + { + thresholdPolicyKickedIn = true; + StringBuilder explanation = new(); + explanation.AppendLine(ExtensionResources.FailureThresholdPolicy); + if (maxPercentage is not null) + { + double failedPercentage = Math.Round((double)(retryFailedTestsPipeServer.FailedUID!.Count / (double)retryFailedTestsPipeServer.TotalTestRan * 100), 2); + explanation.AppendLine(string.Format(CultureInfo.InvariantCulture, ExtensionResources.FailureThresholdPolicyMaxPercentage, maxPercentage, failedPercentage, retryFailedTestsPipeServer.FailedUID.Count, retryFailedTestsPipeServer.TotalTestRan)); + } + + if (maxCount is not null) + { + explanation.AppendLine(string.Format(CultureInfo.InvariantCulture, ExtensionResources.FailureThresholdPolicyMaxCount, maxCount, retryFailedTestsPipeServer.FailedUID!.Count)); + } + + await outputDevice.DisplayAsync(this, new ErrorMessageOutputDeviceData(explanation.ToString())); + break; + } + } + } + + finalArguments.Clear(); + lastListOfFailedId = retryFailedTestsPipeServer.FailedUID?.ToArray(); + continue; + } + else + { + break; + } + } + + if (!thresholdPolicyKickedIn && !retryInterrupted) + { + if (exitCodes[^1] != ExitCodes.Success) + { + await outputDevice.DisplayAsync(this, new ErrorMessageOutputDeviceData(string.Format(CultureInfo.InvariantCulture, ExtensionResources.TestSuiteFailedInAllAttempts, userMaxRetryCount + 1))); + } + else + { + await outputDevice.DisplayAsync(this, new FormattedTextOutputDeviceData(string.Format(CultureInfo.InvariantCulture, ExtensionResources.TestSuiteCompletedSuccessfully, attemptCount)) { ForegroundColor = new SystemConsoleColor { ConsoleColor = ConsoleColor.Green } }); + } + } + + ApplicationStateGuard.Ensure(currentTryResultFolder is not null); + + string[] filesToMove = Directory.GetFiles(currentTryResultFolder, "*.*", SearchOption.AllDirectories); + if (filesToMove.Length > 0) + { + await outputDevice.DisplayAsync(this, new TextOutputDeviceData(ExtensionResources.MoveFiles)); + + // Move last attempt assets + foreach (string file in filesToMove) + { + string finalFileLocation = file.Replace(currentTryResultFolder, resultDirectory); + + // Create the directory if missing + Directory.CreateDirectory(Path.GetDirectoryName(finalFileLocation)!); + + await outputDevice.DisplayAsync(this, new TextOutputDeviceData(string.Format(CultureInfo.InvariantCulture, ExtensionResources.MovingFileToLocation, file, finalFileLocation))); +#if NETCOREAPP + File.Move(file, finalFileLocation, overwrite: true); +#else + File.Copy(file, finalFileLocation, overwrite: true); + File.Delete(file); +#endif + } + } + + return exitCodes[^1]; + } + + // Copied from HotReloadTestHostTestFrameworkInvoker + private static bool IsHotReloadEnabled(IEnvironment environment) + => environment.GetEnvironmentVariable(EnvironmentVariableConstants.DOTNET_WATCH) == "1" + || environment.GetEnvironmentVariable(EnvironmentVariableConstants.TESTINGPLATFORM_HOTRELOAD_ENABLED) == "1"; + + private static int GetOptionArgumentIndex(string optionName, string[] executableArgs) + { + int index = Array.IndexOf(executableArgs, "-" + optionName); + if (index >= 0) + { + return index; + } + + index = Array.IndexOf(executableArgs, "--" + optionName); + return index >= 0 ? index : -1; + } +} diff --git a/src/Platform/Microsoft.Testing.Extensions.Retry/Serializers/FailedTestRequest.cs b/src/Platform/Microsoft.Testing.Extensions.Retry/Serializers/FailedTestRequest.cs new file mode 100644 index 0000000000..f47a8b2c19 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.Retry/Serializers/FailedTestRequest.cs @@ -0,0 +1,29 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under dual-license. See LICENSE.PLATFORMTOOLS.txt file in the project root for full license information. + +using Microsoft.Testing.Platform.IPC; +using Microsoft.Testing.Platform.IPC.Serializers; + +namespace Microsoft.Testing.Platform.Extensions.RetryFailedTests.Serializers; + +internal sealed class FailedTestRequest(string uid) : IRequest +{ + public string Uid { get; } = uid; +} + +internal sealed class FailedTestRequestSerializer : BaseSerializer, INamedPipeSerializer +{ + public int Id => 1; + + public object Deserialize(Stream stream) + { + string uid = ReadString(stream); + return new FailedTestRequest(uid); + } + + public void Serialize(object obj, Stream stream) + { + var testHostProcessExitRequest = (FailedTestRequest)obj; + WriteString(stream, testHostProcessExitRequest.Uid); + } +} diff --git a/src/Platform/Microsoft.Testing.Extensions.Retry/Serializers/GetListOfFailedTests.cs b/src/Platform/Microsoft.Testing.Extensions.Retry/Serializers/GetListOfFailedTests.cs new file mode 100644 index 0000000000..1e8f38c1a2 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.Retry/Serializers/GetListOfFailedTests.cs @@ -0,0 +1,20 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under dual-license. See LICENSE.PLATFORMTOOLS.txt file in the project root for full license information. + +using Microsoft.Testing.Platform.IPC; +using Microsoft.Testing.Platform.IPC.Serializers; + +namespace Microsoft.Testing.Platform.Extensions.RetryFailedTests.Serializers; + +internal sealed class GetListOfFailedTestsRequest : IRequest; + +internal sealed class GetListOfFailedTestsRequestSerializer : BaseSerializer, INamedPipeSerializer +{ + public int Id => 2; + + public object Deserialize(Stream stream) => new GetListOfFailedTestsRequest(); + + public void Serialize(object obj, Stream stream) + { + } +} diff --git a/src/Platform/Microsoft.Testing.Extensions.Retry/Serializers/GetListOfFailedTestsResponse.cs b/src/Platform/Microsoft.Testing.Extensions.Retry/Serializers/GetListOfFailedTestsResponse.cs new file mode 100644 index 0000000000..e959df04bc --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.Retry/Serializers/GetListOfFailedTestsResponse.cs @@ -0,0 +1,40 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under dual-license. See LICENSE.PLATFORMTOOLS.txt file in the project root for full license information. + +using Microsoft.Testing.Platform.IPC; +using Microsoft.Testing.Platform.IPC.Serializers; + +namespace Microsoft.Testing.Platform.Extensions.RetryFailedTests.Serializers; + +internal sealed class GetListOfFailedTestsResponse(string[] failedTestIds) : IResponse +{ + public string[] FailedTestIds { get; } = failedTestIds; +} + +internal sealed class GetListOfFailedTestsResponseSerializer : BaseSerializer, INamedPipeSerializer +{ + public int Id => 3; + + public object Deserialize(Stream stream) + { + int totalFailedTests = ReadInt(stream); + + string[] testsId = new string[totalFailedTests]; + for (int i = 0; i < totalFailedTests; i++) + { + testsId[i] = ReadString(stream); + } + + return new GetListOfFailedTestsResponse(testsId); + } + + public void Serialize(object obj, Stream stream) + { + var getListOfFailedTestsResponse = (GetListOfFailedTestsResponse)obj; + WriteInt(stream, getListOfFailedTestsResponse.FailedTestIds.Length); + foreach (string testId in getListOfFailedTestsResponse.FailedTestIds) + { + WriteString(stream, testId); + } + } +} diff --git a/src/Platform/Microsoft.Testing.Extensions.Retry/Serializers/TotalTestsRunRequest.cs b/src/Platform/Microsoft.Testing.Extensions.Retry/Serializers/TotalTestsRunRequest.cs new file mode 100644 index 0000000000..bfd5e8b4fb --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.Retry/Serializers/TotalTestsRunRequest.cs @@ -0,0 +1,29 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under dual-license. See LICENSE.PLATFORMTOOLS.txt file in the project root for full license information. + +using Microsoft.Testing.Platform.IPC; +using Microsoft.Testing.Platform.IPC.Serializers; + +namespace Microsoft.Testing.Platform.Extensions.RetryFailedTests.Serializers; + +internal sealed class TotalTestsRunRequest(int totalTests) : IRequest +{ + public int TotalTests { get; } = totalTests; +} + +internal sealed class TotalTestsRunRequestSerializer : BaseSerializer, INamedPipeSerializer +{ + public int Id => 4; + + public object Deserialize(Stream stream) + { + int totalTestRun = ReadInt(stream); + return new TotalTestsRunRequest(totalTestRun); + } + + public void Serialize(object obj, Stream stream) + { + var totalTestsRunRequest = (TotalTestsRunRequest)obj; + WriteInt(stream, totalTestsRunRequest.TotalTests); + } +} diff --git a/src/Platform/Microsoft.Testing.Extensions.Retry/TestingPlatformBuilderHook.cs b/src/Platform/Microsoft.Testing.Extensions.Retry/TestingPlatformBuilderHook.cs new file mode 100644 index 0000000000..5e4b1dfe26 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.Retry/TestingPlatformBuilderHook.cs @@ -0,0 +1,12 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under dual-license. See LICENSE.PLATFORMTOOLS.txt file in the project root for full license information. + +using Microsoft.Testing.Platform.Builder; + +namespace Microsoft.Testing.Extensions.Retry; + +public static class TestingPlatformBuilderHook +{ + public static void AddExtensions(ITestApplicationBuilder testApplicationBuilder, string[] _) + => testApplicationBuilder.AddRetryProvider(); +} diff --git a/src/Platform/Microsoft.Testing.Extensions.Retry/build/Microsoft.Testing.Extensions.Retry.props b/src/Platform/Microsoft.Testing.Extensions.Retry/build/Microsoft.Testing.Extensions.Retry.props new file mode 100644 index 0000000000..9e3d7f70c0 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.Retry/build/Microsoft.Testing.Extensions.Retry.props @@ -0,0 +1,3 @@ + + + diff --git a/src/Platform/Microsoft.Testing.Extensions.Retry/buildMultiTargeting/Microsoft.Testing.Extensions.Retry.props b/src/Platform/Microsoft.Testing.Extensions.Retry/buildMultiTargeting/Microsoft.Testing.Extensions.Retry.props new file mode 100644 index 0000000000..8247127236 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.Retry/buildMultiTargeting/Microsoft.Testing.Extensions.Retry.props @@ -0,0 +1,9 @@ + + + + + Microsoft.Testing.Extensions.Retry + Microsoft.Testing.Extensions.Retry.TestingPlatformBuilderHook + + + diff --git a/src/Platform/Microsoft.Testing.Extensions.Retry/buildTransitive/Microsoft.Testing.Extensions.Retry.props b/src/Platform/Microsoft.Testing.Extensions.Retry/buildTransitive/Microsoft.Testing.Extensions.Retry.props new file mode 100644 index 0000000000..9e3d7f70c0 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.Retry/buildTransitive/Microsoft.Testing.Extensions.Retry.props @@ -0,0 +1,3 @@ + + + diff --git a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/NativeAotTests.cs b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/NativeAotTests.cs index c32d56e656..996d1c683e 100644 --- a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/NativeAotTests.cs +++ b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/NativeAotTests.cs @@ -104,7 +104,6 @@ await RetryHelper.RetryAsync( "NativeAotTests", SourceCode .PatchCodeWithReplace("$MicrosoftTestingPlatformVersion$", MicrosoftTestingPlatformVersion) - .PatchCodeWithReplace("$MicrosoftTestingEnterpriseExtensionsVersion$", MicrosoftTestingEnterpriseExtensionsVersion) .PatchCodeWithReplace("$TargetFramework$", TargetFrameworks.NetCurrent) .PatchCodeWithReplace("$MSTestVersion$", MSTestVersion) .PatchCodeWithReplace("$MSTestEngineVersion$", MSTestEngineVersion), diff --git a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/DiagnosticTests.cs b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/DiagnosticTests.cs index d584878a83..2bf2316c5b 100644 --- a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/DiagnosticTests.cs +++ b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/DiagnosticTests.cs @@ -295,8 +295,7 @@ public Task ExecuteRequestAsync(ExecuteRequestContext context) yield return (AssetName, AssetName, TestCode .PatchTargetFrameworks(TargetFrameworks.All) - .PatchCodeWithReplace("$MicrosoftTestingPlatformVersion$", MicrosoftTestingPlatformVersion) - .PatchCodeWithReplace("$MicrosoftTestingEnterpriseExtensionsVersion$", MicrosoftTestingEnterpriseExtensionsVersion)); + .PatchCodeWithReplace("$MicrosoftTestingPlatformVersion$", MicrosoftTestingPlatformVersion)); } } } diff --git a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/ExecutionTests.cs b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/ExecutionTests.cs index 235c2c376d..55fdea8a53 100644 --- a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/ExecutionTests.cs +++ b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/ExecutionTests.cs @@ -303,14 +303,12 @@ internal class Capabilities : ITestFrameworkCapabilities yield return (AssetName, AssetName, TestCode .PatchTargetFrameworks(TargetFrameworks.All) - .PatchCodeWithReplace("$MicrosoftTestingPlatformVersion$", MicrosoftTestingPlatformVersion) - .PatchCodeWithReplace("$MicrosoftTestingEnterpriseExtensionsVersion$", MicrosoftTestingEnterpriseExtensionsVersion)); + .PatchCodeWithReplace("$MicrosoftTestingPlatformVersion$", MicrosoftTestingPlatformVersion)); yield return (AssetName2, AssetName2, TestCode2 .PatchTargetFrameworks(TargetFrameworks.All) - .PatchCodeWithReplace("$MicrosoftTestingPlatformVersion$", MicrosoftTestingPlatformVersion) - .PatchCodeWithReplace("$MicrosoftTestingEnterpriseExtensionsVersion$", MicrosoftTestingEnterpriseExtensionsVersion)); + .PatchCodeWithReplace("$MicrosoftTestingPlatformVersion$", MicrosoftTestingPlatformVersion)); } } } diff --git a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/HelpInfoTests.cs b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/HelpInfoTests.cs index 5c6f2f5b98..4006d2af14 100644 --- a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/HelpInfoTests.cs +++ b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/HelpInfoTests.cs @@ -655,8 +655,8 @@ public sealed class TestAssetFixture() : TestAssetFixtureBase(AcceptanceFixture. - - + + @@ -773,13 +773,11 @@ public Task ExecuteRequestAsync(ExecuteRequestContext context) yield return (NoExtensionAssetName, NoExtensionAssetName, NoExtensionTestCode .PatchTargetFrameworks(TargetFrameworks.All) - .PatchCodeWithReplace("$MicrosoftTestingPlatformVersion$", MicrosoftTestingPlatformVersion) - .PatchCodeWithReplace("$MicrosoftTestingEnterpriseExtensionsVersion$", MicrosoftTestingEnterpriseExtensionsVersion)); + .PatchCodeWithReplace("$MicrosoftTestingPlatformVersion$", MicrosoftTestingPlatformVersion)); yield return (AllExtensionsAssetName, AllExtensionsAssetName, AllExtensionsTestCode .PatchTargetFrameworks(TargetFrameworks.All) - .PatchCodeWithReplace("$MicrosoftTestingPlatformVersion$", MicrosoftTestingPlatformVersion) - .PatchCodeWithReplace("$MicrosoftTestingEnterpriseExtensionsVersion$", MicrosoftTestingEnterpriseExtensionsVersion)); + .PatchCodeWithReplace("$MicrosoftTestingPlatformVersion$", MicrosoftTestingPlatformVersion)); } } } 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 7c64902ec6..5ccb4afda2 100644 --- a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/Helpers/AcceptanceTestBase.cs +++ b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/Helpers/AcceptanceTestBase.cs @@ -55,10 +55,8 @@ static AcceptanceTestBase() MicrosoftNETTestSdkVersion = cpmPropFileDoc.Descendants("MicrosoftNETTestSdkVersion").Single().Value; var versionsPropsFileDoc = XDocument.Load(Path.Combine(RootFinder.Find(), "eng", "Versions.props")); - var directoryPackagesPropsFileDoc = XDocument.Load(Path.Combine(RootFinder.Find(), "Directory.Packages.props")); MSTestVersion = ExtractVersionFromPackage(Constants.ArtifactsPackagesShipping, "MSTest.TestFramework."); MicrosoftTestingPlatformVersion = ExtractVersionFromPackage(Constants.ArtifactsPackagesShipping, "Microsoft.Testing.Platform."); - MicrosoftTestingEnterpriseExtensionsVersion = ExtractVersionFromXmlFile(versionsPropsFileDoc, "MicrosoftTestingExtensionsRetryVersion"); MSTestEngineVersion = ExtractVersionFromXmlFile(versionsPropsFileDoc, "MSTestEngineVersion"); } @@ -81,8 +79,6 @@ static AcceptanceTestBase() public static string MicrosoftTestingPlatformVersion { get; private set; } - public static string MicrosoftTestingEnterpriseExtensionsVersion { get; private set; } - [ClassInitialize(InheritanceBehavior.BeforeEachDerivedClass)] [SuppressMessage("Design", "CA1000:Do not declare static members on generic types", Justification = "Fine in this context")] public static async Task ClassInitialize(TestContext testContext) 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 8ef8d9c03c..6c80392710 100644 --- a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/MSBuild.KnownExtensionRegistration.cs +++ b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/MSBuild.KnownExtensionRegistration.cs @@ -18,8 +18,7 @@ public async Task Microsoft_Testing_Platform_Extensions_ShouldBe_Correctly_Regis nameof(Microsoft_Testing_Platform_Extensions_ShouldBe_Correctly_Registered), SourceCode .PatchCodeWithReplace("$TargetFrameworks$", tfm) - .PatchCodeWithReplace("$MicrosoftTestingPlatformVersion$", MicrosoftTestingPlatformVersion) - .PatchCodeWithReplace("$MicrosoftTestingEnterpriseExtensionsVersion$", MicrosoftTestingEnterpriseExtensionsVersion)); + .PatchCodeWithReplace("$MicrosoftTestingPlatformVersion$", MicrosoftTestingPlatformVersion)); string binlogFile = Path.Combine(testAsset.TargetAssetPath, Guid.NewGuid().ToString("N"), "msbuild.binlog"); await DotnetCli.RunAsync($"restore -r {RID} {testAsset.TargetAssetPath}{Path.DirectorySeparatorChar}MSBuildTests.csproj", AcceptanceFixture.NuGetGlobalPackagesFolder.Path); await DotnetCli.RunAsync($"{(verb == Verb.publish ? $"publish -f {tfm}" : "build")} -c {compilationMode} -r {RID} -nodeReuse:false -bl:{binlogFile} {testAsset.TargetAssetPath} -v:n", AcceptanceFixture.NuGetGlobalPackagesFolder.Path); @@ -69,10 +68,8 @@ public async Task Microsoft_Testing_Platform_Extensions_ShouldBe_Correctly_Regis - - - - + + 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 8040163b92..12f1a09318 100644 --- a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/MSBuildTests.ConfigurationFile.cs +++ b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/MSBuildTests.ConfigurationFile.cs @@ -17,8 +17,7 @@ public async Task ConfigFileGeneration_CorrectlyCreateAndCacheAndCleaned(string SourceCode .PatchCodeWithReplace("$TargetFrameworks$", tfm) .PatchCodeWithReplace("$JsonContent$", ConfigurationContent) - .PatchCodeWithReplace("$MicrosoftTestingPlatformVersion$", MicrosoftTestingPlatformVersion) - .PatchCodeWithReplace("$MicrosoftTestingEnterpriseExtensionsVersion$", MicrosoftTestingEnterpriseExtensionsVersion)); + .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); @@ -56,8 +55,7 @@ public async Task ConfigFileGeneration_NoConfigurationFile_TaskWontRun(string tf SourceCode .PatchCodeWithReplace("$TargetFrameworks$", tfm) .PatchCodeWithReplace("$JsonContent$", ConfigurationContent) - .PatchCodeWithReplace("$MicrosoftTestingPlatformVersion$", MicrosoftTestingPlatformVersion) - .PatchCodeWithReplace("$MicrosoftTestingEnterpriseExtensionsVersion$", MicrosoftTestingEnterpriseExtensionsVersion)); + .PatchCodeWithReplace("$MicrosoftTestingPlatformVersion$", MicrosoftTestingPlatformVersion)); File.Delete(Path.Combine(testAsset.TargetAssetPath, "testconfig.json")); 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 9f87d0df98..80e7b6fb18 100644 --- a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/MSBuildTests.GenerateEntryPoint.cs +++ b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/MSBuildTests.GenerateEntryPoint.cs @@ -113,9 +113,7 @@ private async Task GenerateAndVerifyLanguageSpecificEntryPoint(string assetName, { string finalSourceCode = sourceCode .PatchCodeWithReplace("$TargetFrameworks$", tfm) - .PatchCodeWithReplace("$MicrosoftTestingPlatformVersion$", MicrosoftTestingPlatformVersion) - // We need to pickup local build packages -dev - .PatchCodeWithReplace("$MicrosoftTestingEnterpriseExtensionsVersion$", MicrosoftTestingPlatformVersion); + .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); string binlogFile = Path.Combine(testAsset.TargetAssetPath, Guid.NewGuid().ToString("N"), "msbuild.binlog"); 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 936bc59983..f4afa0443f 100644 --- a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/MSBuildTests.Solution.cs +++ b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/MSBuildTests.Solution.cs @@ -31,8 +31,7 @@ public async Task MSBuildTests_UseMSBuildTestInfrastructure_Should_Run_Solution_ AssetName, SourceCode .PatchCodeWithReplace("$TargetFrameworks$", isMultiTfm ? $"{singleTfmOrMultiTfm}" : $"{singleTfmOrMultiTfm}") - .PatchCodeWithReplace("$MicrosoftTestingPlatformVersion$", MicrosoftTestingPlatformVersion) - .PatchCodeWithReplace("$MicrosoftTestingEnterpriseExtensionsVersion$", MicrosoftTestingEnterpriseExtensionsVersion)); + .PatchCodeWithReplace("$MicrosoftTestingPlatformVersion$", MicrosoftTestingPlatformVersion)); string projectContent = File.ReadAllText(Directory.GetFiles(generator.TargetAssetPath, "MSBuildTests.csproj", SearchOption.AllDirectories).Single()); string programSourceContent = File.ReadAllText(Directory.GetFiles(generator.TargetAssetPath, "Program.cs", SearchOption.AllDirectories).Single()); diff --git a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/Properties/launchSettings.json b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/Properties/launchSettings.json index aefd9415fa..61a6b36f2f 100644 --- a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/Properties/launchSettings.json +++ b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/Properties/launchSettings.json @@ -2,7 +2,7 @@ "profiles": { "Microsoft.Testing.Platform.Acceptance.IntegrationTests": { "commandName": "Project", - "commandLineArgs": "", + "commandLineArgs": "--filter ClassName~RetryFailedTestsTests", "environmentVariables": { //"TESTINGPLATFORM_HOTRELOAD_ENABLED": "1" } diff --git a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/RetryFailedTestsTests.cs b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/RetryFailedTestsTests.cs new file mode 100644 index 0000000000..e49d603001 --- /dev/null +++ b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/RetryFailedTestsTests.cs @@ -0,0 +1,366 @@ +#pragma warning disable IDE0073 // The file header does not match the required text +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under dual-license. See LICENSE.PLATFORMTOOLS.txt file in the project root for full license information. +#pragma warning restore IDE0073 // The file header does not match the required text + +namespace Microsoft.Testing.Platform.Acceptance.IntegrationTests; + +[TestClass] +public class RetryFailedTestsTests : AcceptanceTestBase +{ + private const string AssetName = "RetryFailedTests"; + + internal static IEnumerable<(string Arguments, bool FailOnly)> GetMatrix() + { + foreach (string tfm in TargetFrameworks.All) + { + foreach (bool failOnly in new[] { true, false }) + { + yield return (tfm, failOnly); + } + } + } + + [TestMethod] + [DynamicData(nameof(GetMatrix), DynamicDataSourceType.Method)] + public async Task RetryFailedTests_OnlyRetryTimes_Succeeds(string tfm, bool failOnly) + { + 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}", + new() + { + { EnvironmentVariableConstants.TESTINGPLATFORM_TELEMETRY_OPTOUT, "1" }, + { "METHOD1", "1" }, + { "FAIL", failOnly ? "1" : "0" }, + { "RESULTDIR", resultDirectory }, + }); + + if (!failOnly) + { + testHostResult.AssertExitCodeIs(ExitCodes.Success); + testHostResult.AssertOutputContains("Tests suite completed successfully in 2 attempts"); + testHostResult.AssertOutputContains("Failed! -"); + testHostResult.AssertOutputContains("Passed! -"); + } + else + { + testHostResult.AssertExitCodeIs(ExitCodes.AtLeastOneTestFailed); + testHostResult.AssertOutputContains("Tests suite failed in all 4 attempts"); + testHostResult.AssertOutputContains("Tests suite failed, total failed tests: 1, exit code: 2, attempt: 1/4"); + testHostResult.AssertOutputContains("Tests suite failed, total failed tests: 1, exit code: 2, attempt: 2/4"); + testHostResult.AssertOutputContains("Tests suite failed, total failed tests: 1, exit code: 2, attempt: 3/4"); + testHostResult.AssertOutputContains("Tests suite failed, total failed tests: 1, exit code: 2, attempt: 4/4"); + testHostResult.AssertOutputDoesNotContain("Tests suite failed, total failed tests: 1, exit code: 2, attempt: 5/4"); + testHostResult.AssertOutputContains("Failed! -"); + } + } + + [TestMethod] + [DynamicData(nameof(GetMatrix), DynamicDataSourceType.Method)] + public async Task RetryFailedTests_MaxPercentage_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 --retry-failed-tests-max-percentage 50 --results-directory {resultDirectory}", + new() + { + { EnvironmentVariableConstants.TESTINGPLATFORM_TELEMETRY_OPTOUT, "1" }, + { "RESULTDIR", resultDirectory }, + { "METHOD1", "1" }, + { fail ? "METHOD2" : "UNUSED", "1" }, + }); + + string retriesPath = Path.Combine(resultDirectory, "Retries"); + Assert.IsTrue(Directory.Exists(retriesPath)); + string[] retriesDirectories = Directory.GetDirectories(retriesPath); + Assert.AreEqual(1, retriesDirectories.Length); + string createdDirName = Path.GetFileName(retriesDirectories[0]); + + // Asserts that we are not using long names, to reduce long path issues. + // See https://github.com/microsoft/testfx/issues/4002 + Assert.AreEqual(5, createdDirName.Length, $"Expected directory '{createdDirName}' to be of length 5."); + + if (fail) + { + testHostResult.AssertExitCodeIs(ExitCodes.AtLeastOneTestFailed); + testHostResult.AssertOutputContains("Failure threshold policy is enabled, failed tests will not be restarted."); + testHostResult.AssertOutputContains("Percentage failed threshold is 50% and 66.67% tests failed (2/3)"); + testHostResult.AssertOutputContains("Failed! -"); + } + else + { + testHostResult.AssertExitCodeIs(ExitCodes.Success); + testHostResult.AssertOutputContains("Tests suite completed successfully in 2 attempts"); + testHostResult.AssertOutputContains("Failed! -"); + testHostResult.AssertOutputContains("Passed! -"); + } + } + + [TestMethod] + [DynamicData(nameof(GetMatrix), DynamicDataSourceType.Method)] + public async Task RetryFailedTests_MaxTestsCount_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 --retry-failed-tests-max-tests 1 --results-directory {resultDirectory}", + new() + { + { EnvironmentVariableConstants.TESTINGPLATFORM_TELEMETRY_OPTOUT, "1" }, + { "RESULTDIR", resultDirectory }, + { "METHOD1", "1" }, + { fail ? "METHOD2" : "UNUSED", "1" }, + }); + + if (fail) + { + testHostResult.AssertExitCodeIs(ExitCodes.AtLeastOneTestFailed); + testHostResult.AssertOutputContains("Failure threshold policy is enabled, failed tests will not be restarted."); + testHostResult.AssertOutputContains("Maximum failed tests threshold is 1 and 2 tests failed"); + testHostResult.AssertOutputContains("Failed! -"); + } + else + { + testHostResult.AssertExitCodeIs(ExitCodes.Success); + testHostResult.AssertOutputContains("Tests suite completed successfully in 2 attempts"); + testHostResult.AssertOutputContains("Failed! -"); + testHostResult.AssertOutputContains("Passed! -"); + } + } + + // We use crash dump, not supported in NetFramework at the moment + [TestMethod] + [DynamicData(nameof(TargetFrameworks.NetForDynamicData), typeof(TargetFrameworks))] + public async Task RetryFailedTests_MoveFiles_Succeeds(string tfm) + => await RetryHelper.RetryAsync( + async () => + { + var testHost = TestInfrastructure.TestHost.LocateFrom(AssetFixture.TargetAssetPath, AssetName, tfm); + string resultDirectory = Path.Combine(testHost.DirectoryName, Guid.NewGuid().ToString("N")); + TestHostResult testHostResult = await testHost.ExecuteAsync( + $"--report-trx --crashdump --retry-failed-tests 1 --results-directory {resultDirectory}", + new() + { + { EnvironmentVariableConstants.TESTINGPLATFORM_TELEMETRY_OPTOUT, "1" }, + { "RESULTDIR", resultDirectory }, + { "CRASH", "1" }, + }); + + testHostResult.AssertExitCodeIs(ExitCodes.TestHostProcessExitedNonGracefully); + + string[] entries = Directory.GetFiles(resultDirectory, "*.*", SearchOption.AllDirectories) + .Where(x => !x.Contains("Retries", StringComparison.OrdinalIgnoreCase)) + .ToArray(); + + // 1 trx file + Assert.AreEqual(1, entries.Count(x => x.EndsWith("trx", StringComparison.OrdinalIgnoreCase))); + + // Number of dmp files seems to differ locally and in CI + int dumpFilesCount = entries.Count(x => x.EndsWith("dmp", StringComparison.OrdinalIgnoreCase)); + + if (dumpFilesCount == 2) + { + // Dump file inside the trx structure + Assert.AreEqual(1, entries.Count(x => x.Contains($"{Path.DirectorySeparatorChar}In{Path.DirectorySeparatorChar}", StringComparison.OrdinalIgnoreCase) && x.EndsWith("dmp", StringComparison.OrdinalIgnoreCase))); + } + else if (dumpFilesCount is 0 or > 2) + { + Assert.Fail($"Expected 1 or 2 dump files, but found {dumpFilesCount}"); + } + }, 3, TimeSpan.FromSeconds(5)); + + public sealed class TestAssetFixture() : TestAssetFixtureBase(AcceptanceFixture.NuGetGlobalPackagesFolder) + { + public string TargetAssetPath => GetAssetPath(AssetName); + + public override IEnumerable<(string ID, string Name, string Code)> GetAssetsToGenerate() + { + yield return (AssetName, AssetName, + TestCode + .PatchTargetFrameworks(TargetFrameworks.All) + .PatchCodeWithReplace("$MicrosoftTestingPlatformVersion$", MicrosoftTestingPlatformVersion)); + } + + private const string TestCode = """ +#file RetryFailedTests.csproj + + + $TargetFrameworks$ + enable + enable + Exe + true + preview + + + + + + + + +#file Program.cs +using Microsoft.Testing.Extensions; +using Microsoft.Testing.Extensions.TrxReport.Abstractions; +using Microsoft.Testing.Platform.Builder; +using Microsoft.Testing.Platform.Capabilities.TestFramework; +using Microsoft.Testing.Platform.Extensions.Messages; +using Microsoft.Testing.Platform.Extensions.TestFramework; +using Microsoft.Testing.Platform.Services; + +public class Program +{ + public static async Task Main(string[] args) + { + ITestApplicationBuilder builder = await TestApplication.CreateBuilderAsync(args); + builder.RegisterTestFramework( + sp => new TestFrameworkCapabilities(new TrxReportCapability()), + (_,__) => new DummyTestFramework()); + builder.AddCrashDumpProvider(); + builder.AddTrxReportProvider(); + builder.AddRetryProvider(); + using ITestApplication app = await builder.BuildAsync(); + return await app.RunAsync(); + } +} + +public class TrxReportCapability : ITrxReportCapability +{ + bool ITrxReportCapability.IsSupported { get; } = true; + void ITrxReportCapability.Enable() + { + } +} + +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 Type[] DataTypesProduced => new[] { typeof(TestNodeUpdateMessage) }; + + public Task IsEnabledAsync() => Task.FromResult(true); + + public Task CreateTestSessionAsync(CreateTestSessionContext context) + => Task.FromResult(new CreateTestSessionResult() { IsSuccess = true }); + + public Task CloseTestSessionAsync(CloseTestSessionContext context) + => Task.FromResult(new CloseTestSessionResult() { IsSuccess = true }); + + public async Task ExecuteRequestAsync(ExecuteRequestContext context) + { + bool fail = Environment.GetEnvironmentVariable("FAIL") == "1"; + // Tests are using this env variable so it won't be null. + string resultDir = Environment.GetEnvironmentVariable("RESULTDIR")!; + bool crash = Environment.GetEnvironmentVariable("CRASH") == "1"; + + if (await 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) })); + } + else + { + await context.MessageBus.PublishAsync(this, new TestNodeUpdateMessage(context.Request.Session.SessionUid, + new TestNode() { Uid = "1", DisplayName = "TestMethod1", Properties = new(new FailedTestNodeStateProperty()) })); + } + + if (await TestMethod2(fail, resultDir)) + { + await context.MessageBus.PublishAsync(this, new TestNodeUpdateMessage(context.Request.Session.SessionUid, + new TestNode() { Uid = "2", DisplayName = "TestMethod2", Properties = new(PassedTestNodeStateProperty.CachedInstance) })); + } + else + { + await context.MessageBus.PublishAsync(this, new TestNodeUpdateMessage(context.Request.Session.SessionUid, + new TestNode() { Uid = "2", DisplayName = "TestMethod2", Properties = new(new FailedTestNodeStateProperty()) })); + } + + if (await TestMethod3(fail, resultDir)) + { + await context.MessageBus.PublishAsync(this, new TestNodeUpdateMessage(context.Request.Session.SessionUid, + new TestNode() { Uid = "3", DisplayName = "TestMethod3", Properties = new(PassedTestNodeStateProperty.CachedInstance) })); + } + else + { + await context.MessageBus.PublishAsync(this, new TestNodeUpdateMessage(context.Request.Session.SessionUid, + new TestNode() { Uid = "3", DisplayName = "TestMethod3", Properties = new(new FailedTestNodeStateProperty()) })); + } + + context.Complete(); + } + + private async Task TestMethod1(bool fail, string resultDir, bool crash) + { + if (crash) + { + Environment.FailFast("CRASH"); + } + + bool envVar = Environment.GetEnvironmentVariable("METHOD1") is null; + + if (envVar) return true; + + string succeededFile = Path.Combine(resultDir, "M1_Succeeds"); + bool fileExits = File.Exists(succeededFile); + bool assert = envVar && fileExits; + + if (!fail) + { + if (fileExits) return true; + if (!assert) File.WriteAllText(succeededFile,""); + } + + return assert; + } + + private async Task TestMethod2(bool fail, string resultDir) + { + bool envVar = Environment.GetEnvironmentVariable("METHOD2") is null; + System.Console.WriteLine("envVar " + envVar); + + if (envVar) return true; + + string succeededFile = Path.Combine(resultDir,"M2_Succeeds"); + bool fileExits = File.Exists(succeededFile); + bool assert = envVar && fileExits; + + if (!fail) + { + if (fileExits) return true; + if (!assert) File.WriteAllText(succeededFile,""); + } + + return assert; + } + + private async Task TestMethod3(bool fail, string resultDir) + { + bool envVar = Environment.GetEnvironmentVariable("METHOD3") is null; + + if (envVar) return true; + + string succeededFile = Path.Combine(resultDir,"M3_Succeeds"); + bool fileExits = File.Exists(succeededFile); + bool assert = envVar && fileExits; + + if (!fail) + { + if (fileExits) return true; + if (!assert) File.WriteAllText(succeededFile,""); + } + + return assert; + } +} +"""; + } +} diff --git a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/TimeoutTests.cs b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/TimeoutTests.cs index 91a7a293f8..0e0bdc1db7 100644 --- a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/TimeoutTests.cs +++ b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/TimeoutTests.cs @@ -160,8 +160,7 @@ public Task ExecuteRequestAsync(ExecuteRequestContext context) yield return (AssetName, AssetName, TestCode .PatchTargetFrameworks(TargetFrameworks.All) - .PatchCodeWithReplace("$MicrosoftTestingPlatformVersion$", MicrosoftTestingPlatformVersion) - .PatchCodeWithReplace("$MicrosoftTestingEnterpriseExtensionsVersion$", MicrosoftTestingEnterpriseExtensionsVersion)); + .PatchCodeWithReplace("$MicrosoftTestingPlatformVersion$", MicrosoftTestingPlatformVersion)); } } } diff --git a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/TrxTests.cs b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/TrxTests.cs index 9c86e5770a..979364b7e4 100644 --- a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/TrxTests.cs +++ b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/TrxTests.cs @@ -347,8 +347,7 @@ public void TestMethod1(string s) yield return (AssetName, AssetName, TestCode .PatchTargetFrameworks(TargetFrameworks.All) - .PatchCodeWithReplace("$MicrosoftTestingPlatformVersion$", MicrosoftTestingPlatformVersion) - .PatchCodeWithReplace("$MicrosoftTestingEnterpriseExtensionsVersion$", MicrosoftTestingEnterpriseExtensionsVersion)); + .PatchCodeWithReplace("$MicrosoftTestingPlatformVersion$", MicrosoftTestingPlatformVersion)); yield return (WithSkippedTest, AssetNameUsingMSTest, MSTestCode .PatchTargetFrameworks(TargetFrameworks.All) diff --git a/test/UnitTests/Microsoft.Testing.Extensions.UnitTests/Microsoft.Testing.Extensions.UnitTests.csproj b/test/UnitTests/Microsoft.Testing.Extensions.UnitTests/Microsoft.Testing.Extensions.UnitTests.csproj index e016c30a3a..35150deb12 100644 --- a/test/UnitTests/Microsoft.Testing.Extensions.UnitTests/Microsoft.Testing.Extensions.UnitTests.csproj +++ b/test/UnitTests/Microsoft.Testing.Extensions.UnitTests/Microsoft.Testing.Extensions.UnitTests.csproj @@ -13,6 +13,9 @@ TargetFramework=netstandard2.0 + + TargetFramework=netstandard2.0 + diff --git a/test/UnitTests/Microsoft.Testing.Extensions.UnitTests/RetryTests.cs b/test/UnitTests/Microsoft.Testing.Extensions.UnitTests/RetryTests.cs new file mode 100644 index 0000000000..5fe152714f --- /dev/null +++ b/test/UnitTests/Microsoft.Testing.Extensions.UnitTests/RetryTests.cs @@ -0,0 +1,115 @@ +#pragma warning disable IDE0073 // The file header does not match the required text +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under dual-license. See LICENSE.PLATFORMTOOLS.txt file in the project root for full license information. +#pragma warning restore IDE0073 // The file header does not match the required text + +using System.Globalization; + +using Microsoft.Testing.Extensions.Policy; +using Microsoft.Testing.Extensions.UnitTests.Helpers; +using Microsoft.Testing.Platform.Extensions.CommandLine; + +namespace Microsoft.Testing.Extensions.UnitTests; + +[TestClass] +public class RetryTests +{ + [DataRow(RetryCommandLineOptionsProvider.RetryFailedTestsOptionName, "32")] + [DataRow(RetryCommandLineOptionsProvider.RetryFailedTestsMaxPercentageOptionName, "32")] + [DataRow(RetryCommandLineOptionsProvider.RetryFailedTestsMaxTestsOptionName, "32")] + [TestMethod] + public async Task IsValid_If_CorrectInteger_Is_Provided_For_RetryOptions(string optionName, string retries) + { + var provider = new RetryCommandLineOptionsProvider(); + CommandLineOption option = provider.GetCommandLineOptions().First(x => x.Name == optionName); + + ValidationResult validateOptionsResult = await provider.ValidateOptionArgumentsAsync(option, [retries]).ConfigureAwait(false); + Assert.IsTrue(validateOptionsResult.IsValid); + Assert.IsTrue(string.IsNullOrEmpty(validateOptionsResult.ErrorMessage)); + } + + [DataRow(RetryCommandLineOptionsProvider.RetryFailedTestsOptionName, "invalid")] + [DataRow(RetryCommandLineOptionsProvider.RetryFailedTestsOptionName, "32.32")] + [DataRow(RetryCommandLineOptionsProvider.RetryFailedTestsMaxPercentageOptionName, "invalid")] + [DataRow(RetryCommandLineOptionsProvider.RetryFailedTestsMaxPercentageOptionName, "32.32")] + [DataRow(RetryCommandLineOptionsProvider.RetryFailedTestsMaxTestsOptionName, "invalid")] + [DataRow(RetryCommandLineOptionsProvider.RetryFailedTestsMaxTestsOptionName, "32.32")] + [TestMethod] + public async Task IsInvalid_If_IncorrectInteger_Is_Provided_For_RetryOptions(string optionName, string retries) + { + var provider = new RetryCommandLineOptionsProvider(); + CommandLineOption option = provider.GetCommandLineOptions().First(x => x.Name == optionName); + + ValidationResult validateOptionsResult = await provider.ValidateOptionArgumentsAsync(option, [retries]).ConfigureAwait(false); + Assert.IsFalse(validateOptionsResult.IsValid); + Assert.AreEqual(string.Format(CultureInfo.CurrentCulture, Policy.Resources.ExtensionResources.RetryFailedTestsOptionSingleIntegerArgumentErrorMessage, optionName), validateOptionsResult.ErrorMessage); + } + + [TestMethod] + public async Task IsInvalid_When_MaxPercentage_MaxTests_BothProvided() + { + var provider = new RetryCommandLineOptionsProvider(); + var options = new Dictionary + { + { RetryCommandLineOptionsProvider.RetryFailedTestsMaxPercentageOptionName, [] }, + { RetryCommandLineOptionsProvider.RetryFailedTestsMaxTestsOptionName, [] }, + }; + + ValidationResult validateOptionsResult = await provider.ValidateCommandLineOptionsAsync(new TestCommandLineOptions(options)).ConfigureAwait(false); + Assert.IsFalse(validateOptionsResult.IsValid); + Assert.AreEqual(string.Format(CultureInfo.CurrentCulture, Policy.Resources.ExtensionResources.RetryFailedTestsPercentageAndCountCannotBeMixedErrorMessage, RetryCommandLineOptionsProvider.RetryFailedTestsMaxPercentageOptionName, RetryCommandLineOptionsProvider.RetryFailedTestsMaxTestsOptionName), validateOptionsResult.ErrorMessage); + } + + [TestMethod] + public async Task IsInvalid_When_MaxPercentage_Provided_But_TestOption_Missing() + { + var provider = new RetryCommandLineOptionsProvider(); + var options = new Dictionary + { + { RetryCommandLineOptionsProvider.RetryFailedTestsMaxPercentageOptionName, [] }, + }; + + ValidationResult validateOptionsResult = await provider.ValidateCommandLineOptionsAsync(new TestCommandLineOptions(options)).ConfigureAwait(false); + Assert.IsFalse(validateOptionsResult.IsValid); + Assert.AreEqual(string.Format(CultureInfo.CurrentCulture, Policy.Resources.ExtensionResources.RetryFailedTestsOptionIsMissingErrorMessage, RetryCommandLineOptionsProvider.RetryFailedTestsMaxPercentageOptionName, RetryCommandLineOptionsProvider.RetryFailedTestsOptionName), validateOptionsResult.ErrorMessage); + } + + [TestMethod] + public async Task IsInvalid_When_MaxTests_Provided_But_TestOption_Missing() + { + var provider = new RetryCommandLineOptionsProvider(); + var options = new Dictionary + { + { RetryCommandLineOptionsProvider.RetryFailedTestsMaxTestsOptionName, [] }, + }; + + ValidationResult validateOptionsResult = await provider.ValidateCommandLineOptionsAsync(new TestCommandLineOptions(options)).ConfigureAwait(false); + Assert.IsFalse(validateOptionsResult.IsValid); + Assert.AreEqual(string.Format(CultureInfo.CurrentCulture, Policy.Resources.ExtensionResources.RetryFailedTestsOptionIsMissingErrorMessage, RetryCommandLineOptionsProvider.RetryFailedTestsMaxTestsOptionName, RetryCommandLineOptionsProvider.RetryFailedTestsOptionName), validateOptionsResult.ErrorMessage); + } + + [DataRow(true, false)] + [DataRow(false, true)] + [TestMethod] + public async Task IsValid_When_TestOption_Provided_With_Either_MaxPercentage_MaxTests_Provided(bool isMaxPercentageSet, bool isMaxTestsSet) + { + var provider = new RetryCommandLineOptionsProvider(); + var options = new Dictionary + { + { RetryCommandLineOptionsProvider.RetryFailedTestsOptionName, [] }, + }; + if (isMaxPercentageSet) + { + options.Add(RetryCommandLineOptionsProvider.RetryFailedTestsMaxPercentageOptionName, []); + } + + if (isMaxTestsSet) + { + options.Add(RetryCommandLineOptionsProvider.RetryFailedTestsMaxTestsOptionName, []); + } + + ValidationResult validateOptionsResult = await provider.ValidateCommandLineOptionsAsync(new TestCommandLineOptions(options)).ConfigureAwait(false); + Assert.IsTrue(validateOptionsResult.IsValid); + Assert.IsTrue(string.IsNullOrEmpty(validateOptionsResult.ErrorMessage)); + } +}